ざっくりReact Hooks入門③

### はじめに
今回はReact Hooksについてのまとめ記事の最終回になります。

# useReducer
stateとdispatch（actionを送信する関数）を返すフックです。
このフックを利用すれば、コンポーネント内でstate管理ができます。
そのためuseStateと似たような機能を持つフックです。

構文
``` const [state, dispatch] = useReducer(reducer, stateの初期値); ```
戻り値のdispatchにactionを渡すとstateが更新されます。
引数のreducerによって更新のされ方が変わります。

### actionとは...
アクション（何が起きたか）ととそれに付随する情報を持つオブジェクトのこと。
例

```
const action = {
  type: 'ADD_TODO', // アクションタイプ
  text: 'Learning React' // 付随情報
};

// useReducerの返り値のdispatchにactionを渡すと、actionが送信さてreducerが実行される
// actionを送信することを「actionをdispatchすると言います」
dispatch(action);
```

### reducerとは...
現在のstateとactionを受け取り、actionに応じて更新したstateを返す関数。
actionをdispatchするとreducerが実行される。
例

```
// アクションタイプがADD_TODOのactionがdispatchされた時、stateを更新して返す
// useReducerの第一引数にこのようなものを渡す想定
function reducer(state, action) {
  switch(action.type) {
    case 'ADD_TODO':
      return [...state, [{ text: action.text, completed:false }]];
    default:
      return state;
  }
}
```

### 使用例
```
import React, { useReducer } from 'react';

// 現在の state と action を受け取り、action に応じて更新した state を返す関数
function reducer(state, action) {
  switch (action.type) {
    case 'INCEREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    case 'RESET':
      return { count: 0 };
    default:
      return state;
  }
}

export default function App() {
  // useReducerの第２引数に { count: 0 } を渡しているので、state の初期値は { count: 0 }
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <>
      <p>count: {state.count}</p>

      {/* { type: 'DECREMENT' } という action を送信する */}
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>

      {/* { type: 'INCEREMENT' } という action を送信する */}
      <button onClick={() => dispatch({ type: 'INCEREMENT' })}>+</button>

      {/* { type: 'RESET' } という action を送信する */}
      <button onClick={() => dispatch({ type: 'RESET' })}>reset</button>
    </>
  );
}
```

### useReducerとuseStateとの使い分け
複雑なstateを扱う時はuseReducerを利用した方が良いです。
それ以外は基本useStateで問題ないです。

「複雑なstate」というのは、下記のように別のstateに依存しているstateを扱う場合を想定しています。
・stateを更新するために、別のstateを参照する必要がある
・stateを更新したら、別のstateも一緒に更新する必要がある
・stateを更新するロジックが複雑

# useContext
### ReactにおけるContextとは...
以下のいずれかを指している
①Propsを利用せずに様々な階層のコンポーネントに値を共有するReactの仕組み
②Propsを利用せずに様々な階層のコンポーネントに値を共有するReactのAPI
③Contextオブジェクトのこと
④Contextオブジェクトの値のこと

今回は①の意味で話を進めます。

### Contextを使用するには...
以下が必要
①Contextオブジェクト
 ->React.createContextの戻り値を指す
②Provider
 ->Contextオブジェクトが保持しているコンポーネントを指す(Providerコンポーネントとも呼ぶ)
③Consumer
 ->Contextオブジェクトから値を首都区しているコンポーネントを指す（Consumerコンポーネントとも呼ぶ）

```
importReact,{createContext,useContext}from'react';

// ①Contextオブジェクト
const MyContext = createContext();
// ③MyContextから値を取得しているのでConsumerである
functionChild1(){
  //<MyContext.Providervalue={name}>のnameは'React'なので、textの値は'React'になる。 
  const text = useContext(MyContext);
  return <h1>{text}</h1>;
}
// MyContextから値を取得していないのでConsumerではない
functionChild2(){
  return <h2>NotConsumer</h2>;
}

export defaultfunctionApp(){
  constname='React';
  return(
    {/*②Provider。valueプロパティの値をConsumerに共有する。*/}
    <MyContext.Providervalue={name}>
      <Child1/>
      <Child2/>
    </MyContext.Provider>
  );
}

```

### Contextを使う理由
①グローバルなstateを作成できるため
②Prop drilling問題（バケツリレー問題）を解決できるため


### useContextとは
Contextオブジェクトから値を取得するフック。
Providerから共有される値を取得するために必要。
簡単にいうと、親からPropsで渡されていないのに、Contextに収容されているデータへよりシンプルにアクセスできる機能です。

構文
``` const Context オブジェクトの値 = useContext(Context オブジェクト); ```
Contextオブジェクトから取得できる値はContextオブジェクトが保持しているProviderのvalueプロパティの値になります。

### 使用例

```
import React, { useState, useContext, createContext } from "react";

// Context オブジェクト
const MyContext = createContext();

export default function App() {
  const [count, setCount] = useState(0);
  const value = {
    name: "soarflat",
    handleClick: () => setCount((count) => count + 1)
  };

  return (
    <div>
      <p>count: {count}</p>
      {/* Provider。value プロパティの値を共有する。 */}
      <MyContext.Provider value={value}>
        <Child />
      </MyContext.Provider>
    </div>
  );
}

function Child() {
  return <GrandChild />;
}

function GrandChild() {
  return <GreatGrandChild />;
}

function GreatGrandChild() {
  // Provider（<MyContext.Provider value={value}>）から
  // Context オブジェクトの値（value プロパティの値）を取得する。
  // そのため、context は
  // {
  //   name: 'soarflat',
  //   handleClick: () => setCount(count => count + 1)
  // }
  // になる。
  const context = useContext(MyContext);

  return (
    <>
      <p>{context.name}</p>
      <button onClick={context.handleClick}>increment</button>
    </>
  );
}
```

### Contextを使うデメリット
下記が考えられるので無闇に使わず、使い所を見極めることがポイント。
①コンポーネントがContextに依存するため、コンポーネントの再利用性が低下する
②Contextオブジェクトの値はグローバルなstateのため、下手に使うとどこで利用されているかがわかりにくくなる


# カスタムフック
自作のフックのことを指す。
具体的にいうと、コンポーネントから切り出したロジックを定義した関数です。

### カスタムフックのメリット
①コンポーネント内のロジックを切り出すことで複数のコンポーネントで再利用できる
②コンポーネントから複雑なロジックを切り出すとコンポーネントの見通しが良くなる

### 使用例
```
import React, { useState } from "react";

// カスタムフック
// カスタムフックの名前はuseから始まる必要があります。
function useCounter(initialCount) {
  const [count, setCount] = useState(initialCount);

  const increment = () => {
    setCount(count + 1);
  };

  const decrement = () => {
    setCount(count - 1);
  };

  return { count, increment, decrement };
}

// 再利用①
function Counter1() {
  const { count, increment } = useCounter(0);

  return (
    <>
      <p>count: {count}</p>
      <button onClick={increment}>+</button>
    </>
  );
}

// 再利用②
function Counter2() {
  const { count, decrement } = useCounter(10);

  return (
    <>
      <p>count: {count}</p>
      <button onClick={decrement}>-</button>
    </>
  );
}

function App() {
  return (
    <div>
      <Counter1 />
      <Counter2 />
    </div>
  );
}

export default App;

```


# 続きの記事
[ざっくりReact Hooks入門①](https://qiita.com/yu_ku/items/ac722715a2f27d9d27a3)
[ざっくりReact Hooks入門②](https://qiita.com/yu_ku/items/048df482e04d1cfdbd22)