【React】useReducerとuseContext

# useReducer

useStateと同様、状態管理に使用するもう一つのフック。

```react

// useStateの場合
const [ state, setState ] = useState(0);

// useReducerの場合
const [ rState, dispatch ] = useReducer( prev => ++prev, 0);

```

**・配列の０番目**
状態管理(ステート)の変数。

**・配列の１番目**
更新用関数。useStateと違い、配列の１番目の更新用関数名は`dispatch`と記載することが一般的。
（useStateのように任意の関数名でも動く。）

dispatchを実行すると、useReducerの引数で記載した`prev => ++prev`が実行される。
<br>
<br>

**ポイント**

`・useStateの場合`　→ 更新用関数の中で、ステートをど更新するか定義する。


`・useReducerの場合` → useReducerの定義で、引数内でステートをどう更新するかを定義する。


# useReducerの特徴

useStateと違い、複雑な処理を行なって更新するのに向いている。

```react

import { useReducer, useState } from "react";

const Example = () => {

  const [ state, setState ] = useState(0);

  const [ rState, dispatch ] = useReducer(
    (prev, action) => {
      if(action === '+') {
        return ++prev ;
      }else {
        return --prev;
      }
    }, 0);

  return (
    <>
      <h3>{state}</h3>
      <button onClick={() => setState(prev => prev + 1)}>+</button>
      <h3>{rState}</h3>
      <button onClick={() => dispatch('+') }>+</button>
      <button onClick={() => dispatch('-') }>-</button>
    </>
  );
};

export default Example;

```

以下のように、関数を分けて定義し、useReducerの第一引数に渡してもOK.

```react
import { useReducer } from "react";

const Example = () => {

  const reducer = (prev, action) => {
      if(action === '+') {
        return ++prev ;
      }else {
        return --prev;
      }
  }


  const [ rState, dispatch ] = useReducer(reducer, 0);

  return (
    <>
      <h3>{state}</h3>
      <button onClick={() => setState(prev => prev + 1)}>+</button>
      <h3>{rState}</h3>
      <button onClick={() => dispatch('+') }>+</button>
      <button onClick={() => dispatch('-') }>-</button>
    </>
  );
};

export default Example;

```
<br>
<br>

# reducer関数の定義

`reducer関数`とは、dispatchによって実行される更新関数のこと。上記の例でいう`redcuer`メソッドがreducer関数に該当する。

このreducer関数には、下記のように記述することが基本である。
```react

    // reducer関数の定義
    // state → 現状のステート
    // action　→ dispatchから受け取った、コンポーネントで実行された操作（データ）
    const reducerFunction = (state, action) => {

    // ステートを更新するための処理

      return newState;        
    };
```
## dispatchとは...?
dispatchとは、<font color="red">引数にactionという、コンポーネントによって実行された操作を受け取る。</font>

→ actionというオブジェクトを引数に代入し、reducer関数に渡す。
第二引数はないので、複数渡すことはできない。

```react
dispatch(action, value) // エラー
```

主に
```react
{type: 捜査の種類, payload: 操作によって取得、また更新されるデータ}
```
のように、typeプロパティ('action'の識別子)とpayloadプロパティ(データ)で構成されることが多い。

**基本的には、**

**<font color="red">①dispatchでreducer関数に対してactionを渡す。</fnot>**

**<font color="red">②reducer関数はactionのtypeやpayloadの状況を利用してstateの更新を行う。</font>**

**という形で利用するのが定石。**
<br>
<br>

## reducer関数とは...?
reducer関数は、<font color="red">stateとactionを引数に受け取り、stateを更新する関数のこと。</font>

## 注意！！
<font color="red">reducer関数は`state`と`action`という２つの引数を受け取ることになっているが、
dispatchは必ず`action`という値（データ）のみを引数に取るので、redcuerが２つだからdispatchも２つという認識は誤り。</font>
<br>
# useContext

useContextは、アプリケーション全体(グローバル)でステートを管理したいときに使用する。



## どういうときに使用するか

useContextがない場合、

```react
// Example.jsx

import Child from "./components/Child";

const Example = () => {
  const value = 'hello'
  return <Child value={value}/>;
};

export default Example;


// Child.jsx
import GrandChild from "./GrandChild";

const Child = ({ value }) => (
  <div style={{ border: "1px solid black", padding: 10 }}>
    <h3>子コンポーネント</h3>
    <GrandChild />
  </div>
);

export default Child;


// GrandChild.jsx

const GrandChild = ({ value }) => {

  return (
      <div style={{ border: "1px solid black" }}>
        <h3>孫コンポーネント</h3>
        {value}
      </div>
  );
};
export default GrandChild;

```
Example.jsxから、子コンポーネントのGrandChild.jsxにpropsを渡したい場合、その間にあるChild.jsxに一度propsを渡すという工程が必要になる。

これをReactでは、`Propsのバケツリレー`と呼び、propsの悪い渡し方の例となっている。
Childでは親のExample.jsxから渡されたpropsは使用しないため、不要なコードにもなる。


そのような場面で、ステートをグローバルに管理しておくことで、どの階層のコンポーネントにもpropsを一発で渡すことができる、useCotnextが便利である。


# 書き方

**Example.jsx**
```react
import Child from "./components/Child";
// createContextをインポート
import { createContext } from "react";

// createContextでグローバルに管理するコンテキストを作成。
export const MyContext = createContext("helio");

const Example = () => {
  const value = 'hello'
  return <Child />;
};

export default Example;
```

**GrandChild.jsx**
```react
// 親コンポーネントで作成したコンテキストをインポート
import { MyContext } from "../Example";
// useContextをインポート
import { useContext } from "react";

const GrandChild = () => {
  // useContextを定義。このとき、引数にコンテキストを代入する。
  const value = useContext(MyContext);

  return (
      <div style={{ border: "1px solid black" }}>
        <h3>孫コンポーネント</h3>
        {value}
      </div>
  );
};
export default GrandChild;

```

# useContextとuseState

useContextを用いて、usaeStateをグローバルに管理できるようにする方法は以下の通り。

**Example.jsx**
```react
import { createContext } from "react";
import { useState } from "react";

// コンテキストの新規作成
export const MyContext = createContext(0);

const Example = () => {
  // ステートの定義
  const [ value, setValue ] = useState(0);

  return (
    <>
    // 作成したコンテキスト名.Provider というコンポーネントを作成。
    // そのコンポーネントのpropsに、グローバル管理をしたい対象のステートを代入する。
    <MyContext.Provider value={[ value, setValue ]}>

　　　　　　　　// コンテキストのコンポーネントタグに囲まれたコンポーネント内で、コンテキストの参照が可能になる。
      <Child />
      <OtherChild />
    </MyContext.Provider>
    </>
  );
};

export default Example;

```

**OtherChild.jsx**

```react
import { useContext } from "react";
import { MyContext } from "../Example";

const OtherChild = () => {
  // 配列の２番目のみを取得したい場合、１番目を何も入力せず、[, ]とする。
  const [, setValue ] = useContext(MyContext);
  
  const clickHandler = (e) => {
    setValue((prev) => prev + 1);
  };

  return (
    <div style={{ border: "1px solid black" }}>
      <h3>他の子コンポーネント</h3>
      <button onClick={clickHandler}>+</button>
    </div>
  );
};

export default OtherChild;
```

**Child.jsx**
```react
import GrandChild from "./GrandChild";

const Child = () => {
  return (
    <div style={{ border: "1px solid black", padding: 10 }}>
      <h3>子コンポーネント</h3>
      <GrandChild />
    </div>
  )
};
export default Child;
```

**GrandChild.jsx**
```react
import { useContext } from "react";
import { MyContext } from "../Example";
const GrandChild = () => {
  // GrandChildはExample.jsxのコンポーネントに囲まれていなかったが、
  // 親コンポーネントであるChild.jsxがコンポーネントで囲まれていたので、GrandChildでも取得が可能となっている。
  const [ value ] = useContext(MyContext);
  return (
    <div style={{ border: "1px solid black" }}>
      <h3>孫コンポーネント</h3>
      {value}
    </div>
  );
};
export default GrandChild;

```

# useContextの注意点

useContextでは、ステートを更新した際、useContextを参照しているコンポーネントが全て`再レンダリング`される。

これは、例え参照している値がステートの変数等ではなくても（更新用の関数などでも）、useContextを参照しているというだけで再レンダリングの対象になる。


# 参考文献

https://zenn.dev/web_tips/articles/0638273b083ec8