React: useReducer [アウトプット全くしてこなかったのでアウトプットする009]

前回useStateをアウトプットしたので今日はuseReducerをアウトプットしていきます。

```tsx
const [state, dispatch] = useReducer(reducer, initialArg, init);
```

上記はuseReducerの基本的な構文です。

useStateと見比べてみます。

```tsx
// useState
const [state, setState] = useState(initialState);

// useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
```

どちらも分割代入で第1引数にはstateを受け取っています。
第2引数は違いますがどちらもstateを更新させるための関数です。

ちなみに
`const [state, setState] = useState(initialState);`の書き方はこの記事を読むとよくわかります。

https://qiita.com/kiyoG/items/f9bf163994f3eb9aa0b3

```tsx
const [state, dispatch] = useReducer(reducer, initialArg, init);
```

useReducerの方には第1引数に以下のようなstateの値を更新させる関数を渡し、

```tsx
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}
```

第2引数に以下のような初期値を渡します。


```tsx
const initialState = {count: 0};
```

全体を見ると以下のようになります。

```tsx
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
```

個人的にuseReducerの第一引数と第二引数を逆にしてくれた方が見やすいと思っています。

```tsx
// 以下は誤った構文だがこちらの方が分割代入のstate(値),dispatch(関数)に
// useReducerの引数の並びを合わせているので見やすい
const [state, dispatch] = useReducer(initialArg,reducer);
```

半年くらい前に読んだ本でuseReducerを使ってuseStateでsetStateするのではなく、useReucerを使用するという方法があったので紹介します。

以下はチェックボックスをuseStateで管理しているあるあるなコードなのですが、ここでuseReducerを使えるそうです。

#### あるあるuseState管理

```tsx
export function CheckBox() {
  const [checked, setChecked] = useState(false);

  return (
    <>
      <input
        type="checkbox"
        checked={checked}
        onChange={() => setChecked((checked) => !checked)}
      />
      {checked ? "checked" : "not checked"}
    </>
  );
}
```

#### useReducerを使って書き換える

```tsx
export function CheckBox() {
  const [checked, toggle] = useReducer(checked => !checked,false);

  return (
    <>
      <input
        type="checkbox"
        checked={checked}
        onChange={() => toggle()}
      />
      {checked ? "checked" : "not checked"}
    </>
  );
}
```

今までuseStateを使用してコンポーネント部分からstateを参照していたのがなくなりかなりスッキリしました。
何をやっているかというと

```tsx
const [checked, toggle] = useReducer(checked => !checked,false);
```

`useReducer()`の第1引数が関数でこれはtoggle関数になります。そして第二引数が初期値のfalseでありこれがcheckedの値になります。

またtrue,falseのtoggleだけでなく数値にも使用できます。

```jsx
export function Numbers() {
  const [number, setNumber] = useReducer(
    (number, newNumber) => number + newNumber,
    0
  );

  return <h1 onClick={() => setNumber(3)}>{number}</h1>;
}
```

さらにオブジェクトの場合、もっと光ります。
以下もオブジェクトをuseStateで管理しているあるあるなコードなのです

```jsx
export function User() {
  const firstUser = {
    id: "1234567",
    firstName: "cawa",
    lastName: "uchi",
    city: "nasu",
    state: "tochigi",
    email: "hogehoge@gmail.com",
    admin: false
  };
  const [user, setUser] = useState(firstUser);

  return (
    <div>
      <h1>
        {user.firstName} {user.lastName} - {user.admin ? "Admin" : "Usser"}
      </h1>
      <p>Email: {user.email}</p>
      <p>
        Location: {user.city}, {user.state}
      </p>
      <button
        onClick={() => {
          setUser({ ...user, admin: true });
        }}
      >
        Make Admin
      </button>
    </div>
  );
}
```

問題は以下の部分でsetUserの中でuserというオブジェクトを参照してスプレッド構文でマージしてそれを返しているのですが、`...user`を書き忘れるとuserの情報は`admin: true`のみになってしまいます。

```jsx
<button
  onClick={() => {
    setUser({ ...user, admin: true });
  }}
>
```

もし以下のようにすると

```jsx
<button
  onClick={() => {
    setUser({ admin: true });
  }}
>
```

以下のようにadmin以外の情報がなくなってしまう

```
user = {
  admin: true
}
```

ここでuseReducerを使用すると

```jsx
export function User() {
  const firstUser = {
    id: "1234567",
    firstName: "cawa",
    lastName: "uchi",
    city: "nasu",
    state: "tochigi",
    email: "hogehoge@gmail.com",
    admin: false
  };
  const [user, setUser] = useReducer(
    (user, newDetails) => ({ ...user, ...newDetails }),
    firstUser
  );

  return (
    <div>
      <h1>
        {user.firstName} {user.lastName} - {user.admin ? "Admin" : "Usser"}
      </h1>
      <p>Email: {user.email}</p>
      <p>
        Location: {user.city}, {user.state}
      </p>
      <button
        onClick={() => {
          setUser({ admin: true });
        }}
      >
        Make Admin
      </button>
    </div>
  );
}
```

setUserからスプレッド構文をなくすことができました

```jsx
<button
  onClick={() => {
    setUser({ admin: true });
  }}
>
```

実務でもどんどんuseReducerを使っていきたいと思いました!

### 参考

https://ja.reactjs.org/docs/hooks-reference.html#usereducer

https://qiita.com/seira/items/2fbad56e84bda885c84c

https://reffect.co.jp/react/react-hook-reducer-understanding

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

https://www.amazon.co.jp/React%E3%83%8F%E3%83%B3%E3%82%BA%E3%82%AA%E3%83%B3%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-%E7%AC%AC2%E7%89%88-%E2%80%95Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA%E3%81%AE%E3%83%99%E3%82%B9%E3%83%88%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9-Alex-Banks/dp/4873119383

強くなりたい!!!!!