React 初心者学習まとめ

# はじめに
React初心者が学習で理解した基本的なことを備忘録として記載していきます。

# 環境
Mac OS: Big Sur 11.6
npm: 8.3.1

# Reactについて

ReactはSPA(シングルページアプリケーション)です。
シングルページアプリケーションについては以下のページに書いてあります。

SPA（シングルページアプリケーション）って何者

https://qiita.com/itmaromaro/items/188eb7e6921a2f714280

ざっくりと言うとページをロードすることなくページが更新されていく仕組みです。
例えばtwitterやFacebookなど。

# Reactプロジェクト作成

Create React Appを使用してアプリを作成します。
```
$ npx create-react-app myapp
```
<br>

# Reactの構成

基本的にこの3つのファイルが基盤でApp.js内に子コンポートネントを配置していく。
```
public
  ∟　index.html ... SPAの基盤となるテンプレートです。基本的には触らない。

src
  ∟　index.js ... index.html内の<div id="root"></div>にApp.jsをレンダリングしている。
  ∟  App.js　 ... コンポーネントの基盤。この配下に子コンポーネントを作っていく。
```

# コンポーネントとは

画面のパーツのこと。
例えば画面のボタンのみを切り出して別ファイルとして管理する。
その中にボタンのクリック処理などを設定する。
パーツとして使えるため再利用等ができる。コンポーネントのファイルを呼び出す場合はimportし、コンポーネント側はexportする。

コンポーネントファイル側でボタンを作成する。ボタンを押すとアラートが実行される。

①コンポーネントファイル作成する /src/components/SpecialButton.js
(src配下に自作でcomponentsフォルダを作成するとする。)

```
export const SpecialButton = () => {
  function buttonImplementing() {
     alert("ボタンが押された！");
  }

  return (
    <button onClick={buttonImplementing} />
  );
};
```

②コンポーネントファイルを設置する。/src/App.js

```
import { SpecialButton } from "./components/SpecialButton"; // インポートする

export const App = () => {
  return (
    <div>
      <SpecialButton /> {/** コンポーネントを設置 */}
    </div>
  );
};
```

# Props

コンポーネント側に値を渡すこと。

①呼び出し元よりPropsを渡す。/src/App.js

```
import { SpecialButton } from './components/SpecialButton';

export const App = () => {
  return (
    <div>
      {/** コンポーネントにmessageとbuttonNameをpropsで値を渡す */}
      <SpecialButton message="ボタンを押しました" buttonName="ボタンの名前" />
    </div>
  );
}
export default App;

```

②コンポーネント側で値をpropsで受け取る。 /src/components/SpecialButton.js
```
export const SpecialButton = (props) => {
  function buttonImplementing() {
     alert(props.message); // messageを受け取ってアラートを実行する。
  }

  return (
    {/** buttonNameを受け取ってボタン名を表示する。 */}
    <button onClick={buttonImplementing} >{props.buttonName}</button>
  );
};
```

# State

コンポーネント内にあるデータの状態を管理することができる。

React Hooks(関数)内のuseStateを使用してStateを扱う。

##### /src/components/SpecialButton.js

```
import { useState } from "react"; // React HooksよりuseStateをインポートする

export const SpecialButton = (props) => {

  // count: 一つ目がコンポネート内で保持されるデータ、
  // setCount: 2つ目がデータをセットする関数
  const [count, setCount] = useState(0); // 初期値に0をセットする。

  function buttonImplementing() {
    setCount((count) => count + 1); // 現在のカウンタをインクリメントし関数でセットする。
    console.log(count); // ボタンを押すとcountの値が0,1,2,3と変わっていく。
  }

  return (
    <button onClick={buttonImplementing} >{props.buttonName}</button>
  );
};

```

# 再レンダリング ／ useEffect

## 再レンダリングの概念
コンポーネント内部でStateが変更されたことにより、コンポネート内の表示が変わるのはDOMが再レンダリングされているため。<br><br>

下の場合はボタンを押すとStateで保持しているcountがカウントアップされ、「カウント数:{count}」の表示が変わる。これもコンポネートの再レンダリングを行い表示を変更している。

```
import { useEffect, useState } from "react";

export const SpecialButton = (props) => {

  const [count, setCount] = useState(0);

  function buttonImplementing() {
    setCount((count) => count + 1); // カウントアップする
  }

  return (
    <div>
      <button onClick={buttonImplementing} >{props.buttonName}</button>
      {/** ボタンが押される度にcountがカウントアップして表示が変わっていく */}
      <p>カウント数：{count}</p>
    </div>
  );
};

```

### 再レンダリングが起きる条件
1. Stateが更新されたコンポーネント
2. Propsが変更されたコンポーネント
3. 再レンダリングされたコンポーネント配下のコンポーネント全て


## useEffect
React Hooksには再レンダリングした際に、あるデータが変更された場合に再レンダリングした後に実行できるuseEffectがある。

下記はコンポネート内のstateのcountに変更があった場合に再レンダリングされた後にアラートが実行される。

```
import { useEffect, useState } from "react"; // useEffectをインポート

export const SpecialButton = (props) => {

  const [count, setCount] = useState(0);

  useEffect( () => {
    alert("再レンダリングされました。"); // 第1引数に実行する処理を記述する。
  }, [count]); // 第一引数を実行するためのトリガーとなる依存データを記述する。
  // countの値が変わった際(つまりボタンを押した際)に第一引数の記述したアラートが実行される。

  function buttonImplementing() {
    setCount((count) => count + 1);
    console.log(count); // ボタンを押すと0,1,2,3と値が変わっていく。
  }

  return (
    <button onClick={buttonImplementing} >{props.buttonName}</button>
  );
};
```
コンポネートは何度も再レンダリングされるので、特定の場合のみ実行したいことがある場合に有効な使い方ができる。

# グローバルのState管理

Stateの値などを小コンポートネントにPropsで渡すことができるが、コンポートネントの構成が深くなると管理が複雑化してしまう。

下の例だとA.jsからH.jsやI.jsまでstate値をPropsで渡すとかなり大変になる。
バケツリレーのようにpropsで値を渡していくことになる。

```
App.js
 ∟ A.js           ... App.js内に配置
   ∟ B.js         ... A.js内に配置
     ∟ C.js       ... B.js内に配置
       ∟ F.js     ... C.js内に配置
         ∟ H.js   ... F.js内に配置
       ∟ G.js     ... C.js内に配置
         ∟ I.js   ... G.js内に配置
 ∟ D.js           ... App.js内に配置
   ∟ E.js         ... D.js内に配置
```

React HooksのuseContextを使用するとグローバルに管理ができて、バケツリレーのようにpropsで値を渡す必要がなくなります。

<br>

SampleFlagという値をグローバルState管理する例を記載します。

フォルダ構成
```
[src]
  ∟ [components]
    ∟ componentA.js ... App.js配下にコンポーネント設置
    ∟ componentB.js ... componentA.js配下にコンポーネント設置
    ∟ componentC.js ... componentB.js配下にコンポーネント設置
    ∟ [providers]
        ∟ SampleFlagProvider.js ... SampleFlagというStateをグローバル管理するファイル
  ∟ App.js
  ∟ index.js
```

①SampleFlagProvider.jsを作成
```
import { createContext, useState } from "react";

export const SampleFlagContext = createContext({});

export const SampleFlagProvider = props => {
    // stateを定義。デフォルトにFALSEをセットする。
    const [ SampleFlag, setSampleFlag ] = useState(false);

    return (
        <SampleFlagContext.Provider value={{ SampleFlag, setSampleFlag }}>
            {props.children}
        </SampleFlagContext.Provider>
    );
}

```

②App.jsにComponentA.jsを配置

```
import { ComponentA } from './components/ComponentA';

export const App = () => {
  return (
    <div>
      <ComponentA />
    </div>
  );
}
```

③ComponentA.jsにComponentB.jsを配置
またグローバルStateを呼び出しする。

```
import { useContext, useEffect, useState } from "react";
import { ComponentB } from "./ComponentB";
import { SampleFlagContext } from "./providers/SampleFlagProvider";

export const ComponentA = (props) => {

  // useContextでグローバルStateを呼び出し
  const { SampleFlag, setSampleFlag } = useContext(SampleFlagContext);

  function alertComponentA() {
    setSampleFlag(true); // SampleFlagにtrueを設定

    if(SampleFlag) {
      alert("SampleFlagはTRUEです!"); // このアラートが実行される
    }else {
      alert("SampleFlagはFALSEです!");
    }
  }

  return (
    <div>
        <p>コンポーネントA</p>
        <button onClick={alertComponentA}>コンポーネントA</button>

        <ComponentB />
    </div>
  );
};

```

④ComponentB.jsにComponentC.jsを配置
```
import { useEffect, useState } from "react";
import { ComponentC } from "./ComponentC";

export const ComponentB = (props) => {

  return (
    <div>
        <p>コンポーネントB</p>
        <ComponentC />
    </div>
  );
};
```

⑤ComponentC.jsでグローバルStateを呼び出す

```
import { useContext, useEffect, useState } from "react";
import { SampleFlagContext } from "./providers/SampleFlagProvider";

export const ComponentC = (props) => {

  const { SampleFlag } = useContext(SampleFlagContext); // useContextでグローバルStateを呼び出し

  function alertComponentC() {

    // 上の階層のコンポーネント(ComponentA)でTRUEを設定している
    if(SampleFlag) {
      alert("SampleFlagはTRUEです!"); // このアラートが実行される
    }else {
      alert("SampleFlagはFALSEです!");
    }
  }

  return (
    <div>
        <p>コンポーネントC</p>
        <button onClick={alertComponentC}>ボタン</button>
    </div>
  );
};
```

デフォルトはFALSEだが親コンポーネントComponentA.jsでTRUEを設定しているため、
ComponentC.jsのボタンを押すと「"SampleFlagはTRUEです!"」のアラートが実行される。