はじめに
この記事は、これからReactを学びたい方向けに書かれています。
複数記事の構成になっているので、#1から始めることを推奨します。
また、Reactが初めてであれば、環境構築なしでReactについて学べる以下の記事から始めると、
より躓かずに進められるかと思います!
Reactの公式チュートリアルがCreat React Appからフレームワーク推奨になったことを受け、
Next.js 13を利用してReactを学んでいきます。
本ワークショップのゴール
学べる内容
- Next.jsの環境構築 && コンポーネントについて
-
アトミックデザインとHooksの基礎
今ここ
- API呼び出し(Fetch API)
- スタイリング(Tailwind CSS)
Reactを中心に学んでいく内容となっているので、Next.jsの機能にはあまり触れません。
最終的な成果物
沖縄で開催されるエンジニアイベント一覧
ConnpassのAPIを利用して、沖縄で開催されるイベント情報を取得し、カードUIで表示します。
筆者が沖縄出身であるため「沖縄」にしていますが、絞り込み条件は好みに合わせて後ほど変更してください
一覧を構成するコンポーネントを作成する
おさらいですが、Reactでは JSX(HTMLのようなもの) を返却する関数を コンポーネント と呼びます。
ここでは、イベント一覧画面を構成するためのコンポーネントを作成していきます
が、、、
その前に、アトミックデザイン についての説明をします。
アトミックデザインとは
時間がなかったのでChatGPTに説明してもらいました
アトミックデザインは、ウェブアプリケーションやウェブサイトのデザインプロセスを効率化し、コンポーネントベースのアプローチを促進する方法論です。これは、デザイン要素を小さな部品(Atom)に分解し、それらを組み合わせてより大きなコンポーネント(Molecules)やレイアウト(Organisms)を作り上げるという考え方です。
アトミックデザインでは、デザイン要素を5つの主要なレイヤーに分けて考えます。それぞれのレイヤーは、デザインの階層性と再利用性を表現する役割を果たしています。
アトム(Atoms): アトムは、デザインの基本的な要素です。ボタン、テキストフィールド、アイコンなど、独立した形で存在できる最も小さな要素です。アトムは再利用可能で、一貫性のあるデザインを提供します。
マレキュール(Molecules): マレキュールは、複数のアトムが組み合わさった小さなグループです。例えば、検索フォームやナビゲーションメニューなどがマレキュールに当たります。マレキュールは、アトムの組み合わせによって機能的な単位を形成します。
オーガニズム(Organisms): オーガニズムは、さらに大きなコンポーネントで、マレキュールや他のアトムを組み合わせて作られます。ヘッダーセクションやフッターセクションなどがオーガニズムに該当します。オーガニズムは、ユーザーインタフェースの一部として独立して機能し、複数のマレキュールやアトムを含むことがあります。
テンプレート(Templates): テンプレートは、デザインの具体的な配置と構造を表現します。オーガニズムやマレキュールの配置パターンを定義し、デザインの一貫性を保ちながらコンテンツを組み込むために使用されます。テンプレートは、実際のコンテンツを含まず、デザインのフレームワークを提供します。
ページ(Pages): ページは、実際にユーザーが見ることができる具体的なウェブページや画面です。テンプレートにコンテンツが組み込まれ、実際のユーザーエクスペリエンスが形成されます。ページは、特定のコンテキストや目的に基づいてデザインされ、ユーザーがインタラクションする対象となります。
今回はアトミックデザインに沿って、小さなコンポーネントから作成し、それらを利用して最終的な画面を構成していきます。
まずはcomponents/
ディレクトリを作成し、さらにその配下にコンポーネントを入れていくため
atoms
, molecules
, organisms
, templates
のディレクトリを作成します。
見出しコンポーネントの作成
見出しは、これ以上分解できない小さなatoms
コンポーネントになるので、
components/atoms
ディレクトリの中にHeadline.tsx
を作成します。
export default function Headline() {
return (
<h1>これは見出しコンポーネントです。</h1>
)
}
Headline
コンポーネントは、<h1
>タグを返却する単純なコンポーネントです。
次に、このコンポーネントをイベント一覧画面に表示するため、app/events/list/page.tsx
を編集します。
// 作成した Headline コンポーネントを import する
import Headline from "@/components/atoms/Headline";
export default function EventList() {
return (
<div>
{/* <h1>イベント一覧</h1> を以下に書き換える*/}
<Headline />
</div>
)
}
http://localhost:3000/events/list にアクセスし、見出しが以下のように変わっていればOKです!
ただし、今作ったHeadline
コンポーネントでは、
「これは見出しコンポーネントです。」しか表示することができないので、
せっかくコンポーネントを作っても、他で使い回すことができません。
Headline
コンポーネントで表示する文字列を、使う場所によって動的に変更できるようにしていきます。
以下のようにHeadline
コンポーネントを書き換えます。
export default function Headline(props: {title: string}) {
return (
<h1>{props.title}</h1>
)
}
同じく画面側のapp/events/list/page.tsx
も修正します。
import Headline from "@/components/atoms/Headline";
export default function EventList() {
return (
<div>
<Headline title="ここはイベント一覧画面です" />
</div>
)
}
http://localhost:3000/events/list にアクセスし、
見出しが「ここはイベント一覧画面です」に変わっていればOKです!
Reactのコンポーネントでは、関数の引数のように props(propertiesの略) と呼ばれる変数を利用して、
コンポーネントの外から値を受け取ることができます。
Headline
コンポーネントの例では、title
という props を用意し、表示したい文字列(ここはイベント一覧画面です
)を外から受け取って表示しています。
このようにすることで、Headlineコンポーネントをいろいろな場所で使い回すことができるようになりました!
同じようにカードのコンポーネントを作成していきます。
カードコンポーネントの作成
カードコンポーネントは、タイトルやリンク、枠などに分解することはできるが、分解してもそれ以上の意味を持たないため、molecules
とします。
components/molecules
ディレクトリの中にCard.tsx
を作成し以下をコピペします。
import Link from "next/link"
type CardProps = {
url: string
startAt?: string
title?: string
description?: string
}
export default function Card({url, startAt, title, description}: CardProps) {
return (
<div className="border">
<div>
<span>{startAt}</span>
<h2 >
<Link href={url} target="_blank">{title}</Link>
</h2>
<p className="text-gray-500">{description}</p>
<div>
<Link href={url} target="_blank">詳しく見る</Link>
</div>
</div>
</div>
)
}
今回はpropsとして、url
, startAt
, title
, description
を受け取ることにします。
試しにイベント一覧画面に表示してみましょう。
app/events/list/page.tsx
を以下のように修正します。
import Headline from "@/components/atoms/Headline";
import Card from "@/components/molecules/Card";
export default function EventList() {
return (
<div>
<Headline title="ここはイベント一覧画面です" />
<Card
title="【React入門】絶対に躓かないReact(Next.js)ワークショップ #2"
description="最近フロントエンドを学び始めた人や、これからフロントエンドを学んで行きたい人向けに、Reactのハンズオンを開催します!"
startAt="2023-07-27 19:30"
url="https://okinawa-frontend.connpass.com/event/289268/"
/>
</div>
)
}
http://localhost:3000/events/list にアクセスし、
画面幅いっぱいにカードのコンポーネントが表示されていればOKです!
Hooksの基礎(状態管理について)
Reactでは、アプリケーションの状態を扱うために、Hooksという機能が用意されています。
状態とは、「ローディング中か否か」や「ユーザー入力した文字列」、「取得したデータ」などの情報を指します。
今回は、connpassから取得したイベント情報を画面に表示する部分でHooksを利用していきます!
ここでは、Hooksの中でもよく使われる2つを紹介したいと思います。
useState
まず、Hooksの中でも最もよく使われる、useState
について説明します。
useState
は、状態(state)の保持と更新を行うためのHooksです。
イベント一覧画面にサンプルのコードを追記して、動きを見ていきます。
app/events/list/page.tsx
を以下のように修正します。
ここでは、Next.js の機能であるuse client
には触れません。
おまじないとして進ませて頂きます。気になる方は別途調べて見てください
"use client"
import Headline from "@/components/atoms/Headline";
import Card from "@/components/molecules/Card";
import { useState } from "react";
export default function EventList() {
const [count, setCount] = useState(0)
return (
<div>
<Headline title="ここはイベント一覧画面です" />
<Card
title="【React入門】絶対に躓かないReact(Next.js)ワークショップ #2"
description="最近フロントエンドを学び始めた人や、これからフロントエンドを学んで行きたい人向けに、Reactのハンズオンを開催します!"
startAt="2023-07-27 19:30"
url="https://okinawa-frontend.connpass.com/event/289268/"
/>
<p>count: {count}</p>
<button className="rounded-lg bg-purple-500 px-8 py-3 text-center text-sm hover:bg-purple-600" onClick={() => setCount(count+1)}>カウントアップ</button>
</div>
)
}
「カウントアップ」のボタンを押すと、count: 0
が増えればOKです。
useState
は、状態の初期値を引数とし、[状態, 状態を更新するための関数]
の配列を返却します。
// const [状態, 状態を更新するための関数] = useState(初期値)
const [count, setCount] = useState(0)
サンプルコードの例では、ボタンクリック時にsetCount(count + 1)
を実行しているため、
count
が1ずつ増えていくことになります。
「これなら普通にcount
という変数を宣言して、+1した値を再代入すれば良いのでは?」と思いましたか?鋭いですね!
しかし、それでは画面の表示は変わりません。
Reactでは、状態が変わったときに再レンダリングが行われ、画面表示が変わるようになっていることに注意してください。
useEffect
useEffect
は、コンポーネントを外部システムと同期できるようにするためのHooksです。
もう少し具体的に説明すると、useEffect
の引数に渡した関数を、レンダリングが終わったあとや、依存関係にある変数が変化した時に実行させることができます。
使いどころとしては、画面表示時のAPIの呼び出しなどが該当します。
実際に動きを確認するため、イベント一覧画面にサンプルのコードを追記します。
app/events/list/page.tsx
を以下のように修正します。
"use client"
import Headline from "@/components/atoms/Headline";
import Card from "@/components/molecules/Card";
import { useEffect, useState } from "react";
export default function EventList() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log("countが更新されました")
}, [count])
return (
<div>
<Headline title="ここはイベント一覧画面です" />
<Card
title="【React入門】絶対に躓かないReact(Next.js)ワークショップ #2"
description="最近フロントエンドを学び始めた人や、これからフロントエンドを学んで行きたい人向けに、Reactのハンズオンを開催します!"
startAt="2023-07-27 19:30"
url="https://okinawa-frontend.connpass.com/event/289268/"
/>
<p>count: {count}</p>
<button className="rounded-lg bg-purple-500 px-8 py-3 text-center text-sm hover:bg-purple-600" onClick={() => setCount(count+1)}>カウントアップ</button>
</div>
)
}
「カウントアップ」のボタンをクリックしたときに、コンソールのログに「countが更新されました」と表示されればOKです。
useEffect
は、第一引数に実行したい関数、第二引数に依存する変数の配列を返却します。
useEffect(() => {
// レンダリングが終了時や、依存関係の変化時に実行したい処理
}, [/* 依存する変数 */])
サンプルコードでは、依存する変数にcount
を指定したため、初期レンダリング終了時及びcount
が更新されるたびにconsole.log()
が実行されていることになります。
「ユーザーがボタンを押したときにcount
が+1される」のような直接的な処理に対して、
「ユーザーがボタンを押してcount
が変化したことを検知して、console.log()
を実行する」のようなユーザーの行動とは間接的な処理を、副作用(side-effect) と呼びます。
#2 まとめ
#2 アトミックデザインとHooksの基礎についてでは、
アトミックデザインに沿って、一覧画面に必要なコンポーネントを作成し、
ReactのHooksであるuseState
とuseEffect
について学びました。
次回は、今回学んだHooksを利用してconnpassのAPIを呼び出し、画面に取得したイベント情報を表示していきます。
ぜひいいね・ストックよろしくお願いします!