Reactハンズオン 備忘録 - 実践編

# はじめに
今回のハンズオン勉強会ではレシピ一覧を表示するサンプルコードを用意しました。

## Repository
https://github.com/koki-2424/react-hands-on-learn

モックサーバー、リスト表示コンポーネントが既に実装されているので、そこにRating機能を追加するといった内容のハンズオンを実施しました。

# サンプルコードの実行方法

## 前提条件

- Git
- node.js ≧ v14
    - npm
    - @stoplight/prism-cli（入っていない場合は後の手順で導入）

ローカルにサンプルコードを落とす

以下のコマンドでGitからソースコードを落として、プロジェクトのディレクトリを移動、必要なパッケージのダウンロードまでをやってくれるので、コピペして各自のシェルで実行してください。

```bash
git clone https://github.com/koki-2424/react-hands-on-learn.git
cd react-hands-on-learn/client
npm i
```

プロジェクト構成については以下のようになっていますので、落としたプロジェクトをVS Code（各自のエディター）で開いてみてください。

```
C:\
|
|─client
|  ├─App.jsx # 一番上にあるコンポーネント、ルーティングを実装
|  ├─index.css
|  ├─index.js # entry point for all node apps
│  ├─public
│  └─src
│      ├─api
│      ├─components # UIの部品、各ページで使いまわせるように設計するのが理想
│      └─pages # どのPageコンポーネントを呼ぶかはApp.jsxのpagesコンポーネントで定義している
└─server # モックサーバーを配置
    └─data
```

## モックサーバーの起動

モックサーバーを起動する。
@stoplight/prism-cliは入っていない人のみ実行


```bash
npm install -g @stoplight/prism-cli
npm run server
```

## フロントエンドの起動

```bash
npm run start
```


# Reactコンポーネント

以下のHelloは一番シンプルなReactコンポーネントです。
JSX式で書かれているため見慣れないかもしれませんが、これはシンプルなJavaScript関数です。

以下のHelloコンポーネントはプロパティとしてデータを渡しています。
Reactでは関数でいう引数のことをプロパティと呼び、これはRead onlyなデータとなります。
プロパティだけでは動的なデータをコンポーネントの中で扱うことができず、これだけでは実務で使えるアプリケーションの構築をすることはできません。

```react
// 引数として渡されている"message"がプロパティ
function Hello({message}) {
    return <div>{message}</div>
}

<Hello message="hello world!" />
```

それに対して、描画後に変更できるデータをステートと呼びます。
ステートを導入することで、よりインタラクティブなアプリケーションを構築することができるようになります。

※Reactでは他ファイルで使いたいコンポーネントがあるときは、`export`してファイル外から呼び出し可能にして、`import`することで他ファイルのコンポーネントを呼び出すことができます。サンプルコードには`export`, `import`の記述がありますが、深い意味はなく他ファイルから呼び出すための記述だと理解してください。

# ステート管理

サンプルデータの`value[n].rate`にRating機能のための数値データを用意しました。
このデータを使ってReactのステート管理の仕組みを試しましょう。

## StarRatingコンポーネントを配置する

`react-hands-on-learn\client\src\pages\RecipeList.jsx`で定義したStarRatingコンポーネントを使ってRating機能を実装する。

作成済のコードは星の色を固定値で書いているため、以下のように色を切り替えられるStarコンポーネントを新しく定義してください。

```react
const Star = ({selected = false}) => (
    <FaStar color={selected ? "yellow" : "grey"} />
)
```

上のように定義することでselectedプロパティの値によって異なる色の星を描画することができます。
レーティングのUIは星５であることが多いですが、Starコンポーネントを別に定義しておくことで星１０で細かなレーティングをできるようにしたい。といった要求に答えられるようになります。
新しくStarコンポーネントを定義したのでStarRatingコンポーネントを以下のように書き換えてください。

```react
function StarRating({totalStars = 5}){
  return [...Array(totalStars)].map((_, i) => <Star key={i} />);
}
```

これで、デフォルトで５つの星を表示するコンポーネントが作成できました。
試しにRecipeListコンポーネントに配置してみてください。

## useStateフックを使ってクリックイベントに対応する

関数コンポーネントでステートを実装する場合はReactが提供する**フック**という機能を使います。

> 今回の勉強会では関数コンポーネントについてしか触れていないのですが、Reactにはクラスコンポーネントとして定義する方法もあります。
> クラスコンポーネントを使う場合は、フックではなくライフサイクルメソッドを使ってコンポーネントの状態管理を行います。
> 2019年2月のReactのアップデートがされてからは、クラスコンポーネントではなく、関数コンポーネントを使う方が主流になると考えられます。

ここではStarRatingコンポーネントにステートを追加したいのでuseStateというフックを使います。
以下のようにコードを書き換えてください。

```react
import { useState } from 'react';

const Star = ({ selected = false, onSelect = f => f }) => (
  <FaStar color={selected ? "yellow" : "grey"} onClick={onSelect} />
)

function StarRating({ totalStars = 5 }) {
  // selectedStarsにはデフォルト値"0"がセットされる
  // setSelectedStarsにはselectedStarsを更新するメソッドがセットされる
  const [selectedStars, setSelectedStars] = useState(0);

  return (
    <>
      <div className='flex'>
        {[...Array(totalStars)].map((_, i) => (
          <Star key={i}
            selected={selectedStars > i}
            onSelect={() => setSelectedStars(i + 1)} />
        ))}
      </div>
      <p>{selectedStars} of {totalStars} stars</p>
    </>
  )
}

export default StarRating;
```

上記のコードでは、StarコンポーネントにonSelectプロパティを追加しています。onSelectは関数で、そのままFaStarコンポーネントのonClickイベントのハンドラとして設定されています。
それにより、星のアイコンがクリックされたことを親コンポーネントへ伝えることができる。

上記の例ではuseState()の返り値が二つの変数に代入されている。

```react
  const [selectedStars, setSelectedStars] = useState(0);
```

この書き方はReactで頻繁にされるものででストラクチャリングと呼ばれている。

JavaScript Document: [Destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)

## RecipeListにRating機能を追加してください。

書き換える場所は以下です。

```react
function List({ data = [], renderEmpty }) {
  if (!data) return renderEmpty;
  return (
    <div>
      {data.map((item, i) => (
        <div class="max-w-md py-4 px-8 bg-white shadow-lg rounded-lg my-20">
          <div>
            <h2 class="text-gray-800 text-3xl font-semibold">{item.name}</h2>
            <p class="mt-2 text-gray-600">{item.description}</p>
          </div>
          <div class="flex items-stretch">
            <div className='flex-1 mt-4'><p>{item.rate}</p></div>
            <div className='flex-1 text-right mt-4'>
              <a href="#" class="text-xl font-medium text-indigo-500">Detail</a>
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}
```

## 参考文献

今回の勉強会は「[Reactハンズオンラーニング](https://www.amazon.co.jp/React%E3%83%8F%E3%83%B3%E3%82%BA%E3%82%AA%E3%83%B3%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-%E7%AC%AC2%E7%89%88-%E2%80%95Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA%E3%81%AE%E3%83%99%E3%82%B9%E3%83%88%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9-Alex-Banks/dp/4873119383)」の内容をもとにしています。
本日は「Reactの基本」、「関数の作成」、「ステート管理」から30ページほどを抜き出して学習しました

本の構成としては以下のようになっていてJavaScriptの基礎から学べるので誰にでもおすすめな書籍です。
今回の勉強会で少しでも興味のある方は是非読んでみてください。

- Reactの全体像: 最初５ページ
- JavaScriptの基礎（関数型プログラミング, オブジェクト, 非同期処理...）：約 1 / 6
- Reactの基本から環境構築まで：約 1 / 6ページ
- ステート管理、フック：約 1 / 3
- データ通信について：約 1 / 6
- Reactの将来、Reactライブラリについて：約 1/ 3
    - サスペンス
    - テスト
    - ルーティング
    - サーバーサイド
        - Next.js, `Gatsby`