【React入門】絶対に躓かないReact（Next.js 13）ワークショップ #2

# :beginner: はじめに
この記事は、これからReactを学びたい方向けに書かれています。
複数記事の構成になっているので、[#1](https://qiita.com/daiti0113/items/b864a664454e1c6cc712)から始めることを推奨します。

また、Reactが初めてであれば、環境構築なしでReactについて学べる以下の記事から始めると、
より躓かずに進められるかと思います！

https://qiita.com/kwc_dev2/items/8927a149056f3e66dbf0

Reactの公式チュートリアルがCreat React Appからフレームワーク推奨になったことを受け、
Next.js 13を利用してReactを学んでいきます。

# :round_pushpin: 本ワークショップのゴール

## :pencil: 学べる内容
1. [Next.jsの環境構築 && コンポーネントについて](https://qiita.com/daiti0113/items/b864a664454e1c6cc712)
1. **アトミックデザインとHooksの基礎**　**:point_left: 今ここ**
1. API呼び出し(Fetch API)
1. スタイリング(Tailwind CSS)

:::note warn
Reactを中心に学んでいく内容となっているので、Next.jsの機能にはあまり触れません。
:::

## :calendar_spiral: 最終的な成果物

**沖縄で開催されるエンジニアイベント一覧**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527043/657c3729-23b3-1440-30ad-004117c26ca4.png)
ConnpassのAPIを利用して、沖縄で開催されるイベント情報を取得し、カードUIで表示します。
筆者が沖縄出身であるため「沖縄」にしていますが、絞り込み条件は好みに合わせて後ほど変更してください:grin:


# 一覧を構成するコンポーネントを作成する

おさらいですが、Reactでは **JSX**(HTMLのようなもの) を返却する関数を **コンポーネント** と呼びます。
ここでは、イベント一覧画面を構成するためのコンポーネントを作成していきます :muscle:

が、、、
その前に、**アトミックデザイン** についての説明をします。

## アトミックデザインとは

時間がなかったのでChatGPTに説明してもらいました:wink:

> アトミックデザインは、ウェブアプリケーションやウェブサイトのデザインプロセスを効率化し、コンポーネントベースのアプローチを促進する方法論です。これは、デザイン要素を小さな部品（Atom）に分解し、それらを組み合わせてより大きなコンポーネント（Molecules）やレイアウト（Organisms）を作り上げるという考え方です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527043/8703463a-4e84-cdb0-eb50-a4c0d0616977.png)

> アトミックデザインでは、デザイン要素を5つの主要なレイヤーに分けて考えます。それぞれのレイヤーは、デザインの階層性と再利用性を表現する役割を果たしています。
> 
> アトム（Atoms）: アトムは、デザインの基本的な要素です。ボタン、テキストフィールド、アイコンなど、独立した形で存在できる最も小さな要素です。アトムは再利用可能で、一貫性のあるデザインを提供します。
> 
> マレキュール（Molecules）: マレキュールは、複数のアトムが組み合わさった小さなグループです。例えば、検索フォームやナビゲーションメニューなどがマレキュールに当たります。マレキュールは、アトムの組み合わせによって機能的な単位を形成します。
> 
> オーガニズム（Organisms）: オーガニズムは、さらに大きなコンポーネントで、マレキュールや他のアトムを組み合わせて作られます。ヘッダーセクションやフッターセクションなどがオーガニズムに該当します。オーガニズムは、ユーザーインタフェースの一部として独立して機能し、複数のマレキュールやアトムを含むことがあります。
> 
> テンプレート（Templates）: テンプレートは、デザインの具体的な配置と構造を表現します。オーガニズムやマレキュールの配置パターンを定義し、デザインの一貫性を保ちながらコンテンツを組み込むために使用されます。テンプレートは、実際のコンテンツを含まず、デザインのフレームワークを提供します。
> 
> ページ（Pages）: ページは、実際にユーザーが見ることができる具体的なウェブページや画面です。テンプレートにコンテンツが組み込まれ、実際のユーザーエクスペリエンスが形成されます。ページは、特定のコンテキストや目的に基づいてデザインされ、ユーザーがインタラクションする対象となります。

今回はアトミックデザインに沿って、小さなコンポーネントから作成し、それらを利用して最終的な画面を構成していきます。

まずは`components/`ディレクトリを作成し、さらにその配下にコンポーネントを入れていくため
`atoms`, `molecules`, `organisms`, `templates`のディレクトリを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527043/0f0b1f27-40a4-b417-bc2e-de6ec68b2a88.png)


## 見出しコンポーネントの作成

見出しは、これ以上分解できない小さな`atoms`コンポーネントになるので、
`components/atoms`ディレクトリの中に`Headline.tsx`を作成します。

```tsx:components/atoms/Headline.tsx
export default function Headline() {
  return (
    <h1>これは見出しコンポーネントです。</h1>
  )
}
```

`Headline`コンポーネントは、`<h1`>タグを返却する単純なコンポーネントです。

次に、このコンポーネントをイベント一覧画面に表示するため、`app/events/list/page.tsx`を編集します。

```tsx: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](http://localhost:3000/events/list) にアクセスし、見出しが以下のように変わっていればOKです！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527043/75a7e251-2279-600a-bc20-02a53b82e7e4.png)

ただし、今作った`Headline`コンポーネントでは、
「これは見出しコンポーネントです。」しか表示することができないので、
せっかくコンポーネントを作っても、他で使い回すことができません。
`Headline`コンポーネントで表示する文字列を、使う場所によって**動的に変更できるように**していきます。

以下のように`Headline`コンポーネントを書き換えます。

```tsx:components/atoms/Headline.tsx
export default function Headline(props: {title: string}) {
  return (
    <h1>{props.title}</h1>
  )
}
```

同じく画面側の`app/events/list/page.tsx`も修正します。

```tsx: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](http://localhost:3000/events/list) にアクセスし、
見出しが「ここはイベント一覧画面です」に変わっていればOKです！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527043/de30e826-cd54-6217-71f9-ff48b5b94989.png)


Reactのコンポーネントでは、関数の引数のように **props**(propertiesの略) と呼ばれる変数を利用して、
コンポーネントの外から値を受け取ることができます。
`Headline`コンポーネントの例では、`title`という **props** を用意し、表示したい文字列(`ここはイベント一覧画面です`)を外から受け取って表示しています。
このようにすることで、Headlineコンポーネントをいろいろな場所で使い回すことができるようになりました！

同じようにカードのコンポーネントを作成していきます。

## カードコンポーネントの作成

カードコンポーネントは、タイトルやリンク、枠などに分解することはできるが、分解してもそれ以上の意味を持たないため、`molecules`とします。
`components/molecules`ディレクトリの中に`Card.tsx`を作成し以下をコピペします。

```tsx: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`を以下のように修正します。

```tsx: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](http://localhost:3000/events/list) にアクセスし、
画面幅いっぱいにカードのコンポーネントが表示されていればOKです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527043/a3ea6170-fd5b-9309-68aa-89d25b6165ad.png)


# Hooksの基礎（状態管理について）

Reactでは、アプリケーションの**状態**を扱うために、**Hooks**という機能が用意されています。
**状態**とは、「ローディング中か否か」や「ユーザー入力した文字列」、「取得したデータ」などの情報を指します。

今回は、connpassから取得したイベント情報を画面に表示する部分でHooksを利用していきます！
ここでは、Hooksの中でもよく使われる２つを紹介したいと思います。

## `useState`

まず、Hooksの中でも最もよく使われる、`useState`について説明します。
`useState`は、**状態(state)の保持と更新**を行うためのHooksです。

イベント一覧画面にサンプルのコードを追記して、動きを見ていきます。
`app/events/list/page.tsx`を以下のように修正します。

:::note warn
ここでは、Next.js の機能である`use client`には触れません。
おまじないとして進ませて頂きます。気になる方は別途調べて見てください:bow:
:::

```tsx:app/events/list/page.tsx
"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です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527043/c9aa861c-2a4f-9cd7-0513-387d1470e78b.png)


`useState`は、状態の初期値を引数とし、`[状態, 状態を更新するための関数]`の配列を返却します。

```tsx
// const [状態, 状態を更新するための関数] = useState(初期値)
const [count, setCount] = useState(0)
```

サンプルコードの例では、ボタンクリック時に`setCount(count + 1)`を実行しているため、
`count`が１ずつ増えていくことになります。

「これなら普通に`count`という変数を宣言して、+１した値を再代入すれば良いのでは？」と思いましたか？鋭いですね！
しかし、それでは画面の表示は変わりません。
Reactでは、状態が変わったときに再レンダリングが行われ、画面表示が変わるようになっていることに注意してください。

## `useEffect`

`useEffect`は、**コンポーネントを外部システムと同期できるようにする**ためのHooksです。
もう少し具体的に説明すると、`useEffect`の引数に渡した関数を、**レンダリングが終わったあとや、依存関係にある変数が変化した時**に実行させることができます。

使いどころとしては、**画面表示時のAPIの呼び出し**などが該当します。

実際に動きを確認するため、イベント一覧画面にサンプルのコードを追記します。
`app/events/list/page.tsx`を以下のように修正します。

```tsx: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です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527043/dccb5497-17e6-6ebb-02e8-b7b0f2d501ea.png)


`useEffect`は、第一引数に**実行したい関数**、第二引数に**依存する変数の配列**を返却します。

```tsx
useEffect(() => {
  // レンダリングが終了時や、依存関係の変化時に実行したい処理
}, [/* 依存する変数 */])
```

サンプルコードでは、依存する変数に`count`を指定したため、初期レンダリング終了時及び`count`が更新されるたびに`console.log()`が実行されていることになります。

「ユーザーがボタンを押したときに`count`が+１される」のような直接的な処理に対して、
「ユーザーがボタンを押して`count`が変化したことを検知して、`console.log()`を実行する」のようなユーザーの行動とは間接的な処理を、**副作用(side-effect)** と呼びます。

# #2 まとめ
**#2 アトミックデザインとHooksの基礎について**では、
**アトミックデザイン**に沿って、一覧画面に必要なコンポーネントを作成し、
Reactの**Hooks**である`useState`と`useEffect`について学びました。
次回は、今回学んだHooksを利用してconnpassのAPIを呼び出し、画面に取得したイベント情報を表示していきます。
ぜひいいね・ストックよろしくお願いします！