React 朝の体操

# はじめに
普段は主にバックエンド側を担当していて、フロントは必要に応じて JavaScript、HTML、CSS をいじるくらい。
React はやっておいた方がいいかなと思って、三目並べのチュートリアルに取り組んだり、Udemy、インターネットの記事などを見たり。
実際やってみると React はユニークで面白いなと思いつつ、他のことを勉強していたり、今は業務でも使わないし、少し時間が経つと細かいことを忘れてしまいがち。。
それで、以下のようなことを考えました。
- 三目並べよりもっと簡易に React の練習ができるトレーニングメニューを作って、繰り返しやったら身につくのでは？

そんな思いから、朝のラジオ対応くらいの感覚でできればという意味を込めて「React 朝の体操」というの練習メニューを作ってみました。

※朝の体操シリーズ：[GORM 朝の体操](https://qiita.com/y74h1116/items/fa952d9742bdaf4a239c)

# 対象とする人
- React に習熟したい（React の説明はしないです。自身の血・肉にしたい人を想定）
- なんでもいいから、React の基本的な機能を使ってみたい

# 「React 朝の体操」の練習用 Docker 環境のありか
GitHub に置きました。
ダウンロードまたはクローンして使ってください。
https://github.com/y74h1116/react-taiso
※README.md にコンテナの起動方法を記載してあります。

# 動作環境
Dockerが動いて、Webブラウザがあれば動くと思います。
（こちらでは Mac、WSL で動かしています。）
また、Dockerを使わなくても、オンラインの開発環境サービス、例えば CodeSandbox などでもできるのではと思っています。

# React 朝の体操の内容紹介
前述の GitHub リポジトリに README.md に朝の体操メニューを記載してありますので、その内容に従ってソースコードに追加していきます。
React のデフォルト画面に追加していきます。
トレーニングなので、見た目（デザインやCSS）や変数名にはあまり気を使わずガシガシ打ち込む感じです。

- jsx を使ってみる
![002_hello.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3522449/a9af5a54-a998-40bb-d110-9360a2001f12.png)
下記 Hello001.tsx が実装例です。（ファイル名や表示テキストに001とつけるのは、繰り返し練習する場合、２回目は 002 にすると干渉せずにファイル追加できるためです。）
```src/Hello001.tsx
+ import { FC } from "react";
+
+ export const Hello001:FC = () => {
+    return (
+        <p>hello 001</p>
+    );
+ };
```
Hello001 を表示するため、App.tsx の jsx に `<Hello001/>` を追加します。
```src/App.tsx
  import reactLogo from './assets/react.svg'
  import viteLogo from '/vite.svg'
  import './App.css'
+ import { Hello001 } from './Hello001'

  function App() {
    const [count, setCount] = useState(0)

    return (
      <>
+       <Hello001/>
        <div>
    ～以下略～
```
- Hello001 に枠をつける
![003_border.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3522449/27079dd7-aab1-0817-c2e4-0dc642ef5622.png)
border を指定します。（solidにした）
```src/Hello001.tsx
import { FC } from "react";

export const Hello001:FC = () => {
+    const style = {border: 'solid'};
     return (
+        <div style={style}>
             <p>hello 001</p>
+        </div>
    );
};
```
- useState を使ってみる
![004_input_text.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3522449/4f8ab8c9-1659-500b-ffb8-f57a5fa0a54a.png)
単純に、テキストボックスのコンポーネントで useState を呼び出すのと onChange のハンドラ関数を定義します。
```src/Hello001Text.tsx
+ import { FC, useState } from "react";
+ 
+ export const Hello001Text:FC = () => {
+     const [strInput, setStrInput] = useState('');
+ 
+     const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
+         setStrInput(event.target.value);
+     };
+ 
+     return (
+         <input type="text" value={strInput} onChange={onChangeHandler}/>
+     );
+ };
```
Hello001 に追加します。
```src/Hello001.tsx
  import { FC } from "react";
+ import { Hello001Text } from "./Hello001Text";

  export const Hello001:FC = () => {
      const style = {border: 'solid'};
      return (
          <div style={style}>
              <p>hello 001</p>
+             <Hello001Text/>
          </div>
      );
  };
```
- useReducer を使ってみる
![005_useReducer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3522449/09e0ad3b-c4a0-191c-80cf-7390bbf3c887.png)
ここでは、テキストボックスの内容が変化したら reducer に追加していくようにしました。
テキストボックスの下に表示してます。（テキストの履歴のようになる）
```src/Hello001Text.tsx
  import { FC, useState } from "react";
+ import { FC, useReducer, useState } from "react";

  export const Hello001Text:FC = () => {
      const [strInput, setStrInput] = useState('');

+     const reducerFunc = (state:string, action:string) => {
+         return state + ' => ' + action;
+     };
+     const [reducerState, dispatchFunc] = useReducer(reducerFunc, strInput);

      const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
          setStrInput(event.target.value);
+         dispatchFunc(event.target.value);
      };

      return (
+         <div>
              <input type="text" value={strInput} onChange={onChangeHandler}/>
+             <p>{reducerState}</p>
+         </div>
      );
  };

```
- Recoil を使う準備
RecoilRoot を追加します。
```src/Hello001.tsx
  import { FC } from "react";
  import { Hello001Text } from "./Hello001Text";
+ import { RecoilRoot } from "recoil";
  
  export const Hello001:FC = () => {
      const style = {border: 'solid'};
      return (
          <div style={style}>
              <p>hello 001</p>
              <Hello001Text/>
+             <RecoilRoot>
+             </RecoilRoot>
          </div>
      );
  };
```

- Recoil の Atom を使ってみる
jsx を使っていないので、拡張子は .ts にしました。
キーなどもあまりこだわらず、ファイル名と一緒にしておきます。
```src/Hello001Atom.ts
+ import { atom } from "recoil";
+ 
+ export const Hello001Atom = atom({
+     key: "Hello001Atom",
+     default: 'a',
+ });

```

- atom の値を表示する関数コンポーネントを追加する
![008_atom.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3522449/db9e0b16-542f-4b5a-e5a6-e9ac4531794b.png)
単純に atom の値を取得して出力します。
```src/Hello001Show.tsx
+ import { FC } from "react";
+ import { useRecoilValue } from "recoil";
+ import { Hello001Atom } from "./Hello001Atom";
+ 
+ export const Hello001Show:FC = () => {
+     const strAtom = useRecoilValue(Hello001Atom);
+     return (
+         <p>{strAtom}</p>
+     );
+ };
```
RecoilRoot の中に配置します。
```src/Hello001.tsx
  import { FC } from "react";
  import { Hello001Text } from "./Hello001Text";
  import { RecoilRoot } from "recoil";
+ import { Hello001Show } from "./Hello001Show";

  export const Hello001:FC = () => {
      const style = {border: 'solid'};
      return (
          <div style={style}>
              <p>hello 001</p>
              <Hello001Text/>
              <RecoilRoot>
+                 <Hello001Show/>
              </RecoilRoot>
          </div>
      );
  };

```

- atom の値を更新してみる
![009_button.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3522449/49859ddb-f7c2-e447-44f7-31a1d3681b19.png)
ボタンをクリックしたら、atom の文字列を増やすようにしてみました。
```src/Hello001Button.tsx
+ import { FC } from "react";
+ import { useRecoilState } from "recoil";
+ import { Hello001Atom } from "./Hello001Atom";
+ 
+ export const Hello001Button:FC = () => {
+     const [strAtom, setStrAtom] = useRecoilState(Hello001Atom);
+ 
+     const onClickHandler = () => {
+         setStrAtom(strAtom + ',a');
+     }; 
+ 
+     return (
+         <button onClick={onClickHandler}>ボタン</button>
+     );
+ };

```
ボタンも RecoilRoot の中に配置することを忘れずに。
```src/Hello001.tsx
  import { FC } from "react";
  import { Hello001Text } from "./Hello001Text";
  import { RecoilRoot } from "recoil";
  import { Hello001Show } from "./Hello001Show";
+ import { Hello001Button } from "./Hello001Button";

  export const Hello001:FC = () => {
      const style = {border: 'solid'};
      return (
          <div style={style}>
              <p>hello 001</p>
              <Hello001Text/>
              <RecoilRoot>
                  <Hello001Show/>
+                 <Hello001Button/>
              </RecoilRoot>
          </div>
      );
  };
```

- useEffec を使ってみる
![010_useEffect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3522449/f8fa44ea-eb3c-c7bf-88eb-8595a4b71c35.png)
atomの文字数をタイトルに表示するようにしてみました。
```src/Hello001Button.tsx
+ import { FC, useEffect } from "react";
  import { useRecoilState } from "recoil";
  import { Hello001Atom } from "./Hello001Atom";

  export const Hello001Button:FC = () => {
      const [strAtom, setStrAtom] = useRecoilState(Hello001Atom);

      const onClickHandler = () => {
          setStrAtom(strAtom + ',a');
      }; 

+     useEffect(() => {
+         document.title = 'Hello001Button:' + strAtom.length.toString();
+     }, [strAtom]);

      return (
          <button onClick={onClickHandler}>ボタン</button>
      );
  };

```

# おわりに
もし、もう一回やる場合は、初期状態に戻してもいいですし
Hello002.tsx を作って（内容は Hello001.tsx と同じ）
以下のように Hello001 の上に Hello002 を配置してもいいと思います。
（このようにすると繰り返してやってる感が出るかなと）
```src/App.tsx
  import { useState } from 'react'
  import reactLogo from './assets/react.svg'
  import viteLogo from '/vite.svg'
  import './App.css'
  import { Hello001 } from './Hello001'
+ import { Hello002 } from './Hello002'

  function App() {
    const [count, setCount] = useState(0)

    return (
      <>
+       <Hello002/>
        <Hello001/>
        <div>
        ～以下略～
```