ReactのuseState、useReducerの違いを理解する

## はじめに
Reactの状態管理のためのフックとして、`useState`と`useReducer`の2つがあります。
2つの違いがいまいちピンときておらず、なんとなく`useState`を使えばいいのかな、と思っていました。
なんとなくの理解から確実に自分の知識とするために、それぞれの詳細を確認し、その違いを理解していきます。

## useState
### 基本の形
```react
const [state, setState] = useState(init);
```
#### 引数
`init`:
stateに設定したい初期値。どんな型の値でもよい。関数も可。

#### 戻り値
戻り値は配列で渡されるため、受け取る際は分割代入で受け取るのが一般的。
また、その際の変数名は何でもよいが、状態`xxxx`に対して、関数は`setXxxx`とするのが一般的らしい。

`state`:
現在の状態。一番初めは`init`で設定した値が入る。
`init`に関数を設定した場合は、その関数の戻り値が入る。

`setState`:
新しい状態を設定するための関数。
通常、引数には新しい状態を直接指定する。
前の状態に基づいて新しい状態を設定する場合には、関数を渡すこともできる。
関数を渡す場合は以下
```react
setState(prev => {
  // do something
  return newState;
});
```
`setState`にわたす関数には、前の状態（`prev`）が引数として渡される。これも名前は何でもいい。
そして、新しく設定したい状態をreturnとして返す。
また、`setState`を呼び出すとコンポーネントが再レンダリングされる。

### 使用例
```react:Example.js
import { useState } from "react";
const Example = () => {
    const [count, setCount] = useState(0);
    const countUp = () => setCount(prev => prev + 1);
    const countDown = () => setCount(prev => prev - 1);
    return (
        <>
            <h3>{count}</h3>
            <button type="button" onClick={countUp}>count up</button>
            <button type="button" onClick={countDown}>count down</button>
        </>
    );
};
export default Example;
```
<img width='50%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/b9c799d2-aa0f-348b-6be8-f556bf875770.png'>

[count up]ボタンを押すたび、画面上の値が1ずつインクリメントされ、[count down]ボタンを押すと画面上の値がデクリメントされます。



## useReducer
### 基本の形
```react
const [state, dispatch] = userReducer(reducer, init);
```

#### 引数
`reducer`:
`state`を更新するための関数。
この関数の第一引数には、現在の状態が渡される。第二引数には後述の`dispatch`に渡した引数が設定される。
新しい`state`に設定したい値を戻り値として返すようにする。
```react:reducerの例
const reducer = (state, action){
  switch(action.type) {
    case anything:
      return newState;
  }
}
```

`init`:
`state`に設定したい初期値。

:::note warn
useReducerは第三引数として、初期値を生成する関数を設定できますが、省略可能なためここでは割愛します。
:::


#### 戻り値
状態管理ライブラリであるReduxの影響を受けているらしい。
そのため、更新用関数の変数名は`dispatch`とするのが一般的
`state`:
現在の状態。一番初めは`init`で設定した値が入る。

`dispatch`:
引数で設定した`reducer`を呼び出す。引数に何を設定してもよいが、オブジェクトで渡すのが一般的。
プロパティとして`type`を設定し、それによって処理方法を分岐する。


### 使用例
```react:Example.js
import { useReducer } from "react";

const reducer = (state, action) => {
    switch (action.type) {
        case '+':
            return state + 1;
        case '-':
            return state - 1;
        default:
            return state;
    }
};

const Example = () => {
    const [count, dispatch] = useReducer(reducer, 0);
    const countUp = () => dispatch({ type: '+' });
    const countDown = () => dispatch({ type: '-' });
    return (
        <>
            <h3>{count}</h3>
            <button type="button" onClick={countUp}>count up</button>
            <button type="button" onClick={countDown}>count down</button>
        </>
    );
};
export default Example;
```
実行結果は`useState`のときと同じ

## 2つの共通点
`useState`, `useReducer`の両者とも
* 現在の状態を持つ
* 状態を更新する
* 状態の更新時、コンポーネントを再レンダリングする

ということが共通しています。
そのため、画面の操作によって画面上の値などを変化させたい場合、どちらを使ってもよいように思えます。

## 2つの相違点
決定的な違いは、**更新の方法を決めるタイミング**です。

### useStateの場合
`useState`の戻り値である更新用関数の`setState`を実行するときに`state`をどのように更新するかを決定します。
つまり、コンポーネントの内部で更新方法を決定します。

### useReducerの場合
`useReducer`に`reducer`として関数を渡し、`state`をどのように更新するかを決定します。
`reducer`はコンポーネントの外部で設定することも可能です。
`useReducer`の戻り値である更新用の関数`dispatch`を実行する際には、すでに`reducer`によって更新方法が決定されているため、単にアクションを引数として渡すだけです。

## どちらを使うべきか
基本的には`useState`でよいと考えています。
しかし、複雑なロジックや、互いに関連する状態を管理する場合には`useReducer`のほうがわかりやすくなると思います。

以下のように、入力された項目と、カウント数を表示する画面があるとします。
<img width='50%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/fc83c1e7-ce2a-7669-3045-5a5b5f729e3d.png'>

これを`useReducer`を使って表現すると以下のようになります。
```react:Example.js
import { useReducer } from "react";
const ACTIONS = {
    PLUS: 'PLUS'
    , MINUS: 'MINUS'
    , CHANGE_TXT: 'CHANGE_TXT'
}
const INIT_DATA = { text: 'apple', count: 0 };

const reducer = (state, action) => {
    switch (action.type) {
        case 'PLUS':
            return { ...state, count: state.count + 1 };
        case 'MINUS':
            return { ...state, count: state.count - 1 };
        case 'CHANGE_TXT':
            return { ...state, text: action.text };
        default:
            return state;
    }
};

const Example = () => {
    const [state, dispatch] = useReducer(reducer, INIT_DATA);
    const countUp = () => dispatch({ type: ACTIONS.PLUS });
    const countDown = () => dispatch({ type: ACTIONS.MINUS });
    const changeText = (e) => dispatch({ type: ACTIONS.CHANGE_TXT, text: e.target.value })
    return (
        <>
            <h3>{state.text}:{state.count}</h3>
            <button type="button" onClick={countUp}>+</button>
            <button type="button" onClick={countDown}>-</button>
            <input type="text" value={state.text} onChange={changeText}></input>
        </>
    );
};

export default Example;
```
カウントの増減、テキストの変更の処理がすべて`reducer`の中にまとまっており、シンプルです。

同じ処理を`useState`を使って実装すると、更新用関数である`setState`の呼び出し時に処理を記載することになるため、処理がバラバラの場所に記載されます。

## まとめ
`useState`と`useReducer`について、基本的には実現できることは変わらないことがわかりました。
通常はだいたい`useState`でよさそうですが、複雑な処理や複数の状態を管理する場合には、`useReducer`の方がより理解しやすいといえます。