React, Redux 初心者が、Hooks 時代の React, Redux, React-Redux に触れてみて感じたこと 

筆者は、 Reactも Reduxも React-Reduxも初心者で、プライベートでちょっと触っている程度のペーペーです。

React に Hooks が導入されてしばらく経ちますが、React-Reduxにも Hooks が導入されていることを最近知りました。ちょっと試して見たところ、Hooksを使うことで少しだけモヤモヤが晴れてきたので、これまで理解したことを整理したいと思います。

# useState, useReducer は（規模の大きな開発には）使わない

`useState`, `useReducer` はとっつきやすくてわかりやすいです。なので、ちょっとしたプログラムでは使うと思います。便利です。Youtubeの[React Today and Tomorrow and 90% Cleaner React With Hooks](https://www.youtube.com/watch?v=dpw9EHDh2bM) で、初めてこれを知った時は、感動すら、覚えました。

ですが、今回、自分でちょっと触ってみて、複数のコンポーネントにまたがるような情報を管理する必要が出てくると面倒そうだと感じました。
また、 テストも面倒なんじゃないかなと思ったりしてます。

以下が、毎度お馴染みの、 `useState` を使った実装例です。

```react:src/ComponentUseState.js
import React, { useState } from "react";

const ComponentUseState = () => {
  const [num, setNum] = useState(0);

  return (
    <div>
      <h2>Using useState</h2>
      Number: {num}
      <button onClick={() => setNum(num + 1)}>+</button>
      <button onClick={() => setNum(num - 1)}>-</button>
    </div>
  );
};

export default ComponentUseState;
```

シンプルに書けるのですが、足し算、引き算の処理（ロジック）が、 コンポーネントの中に含まれている状態で、ちょっとテストが面倒そうです。
また、別のコンポーネントから、 `num` を参照したり、変更したりするのが面倒そうです。

`useReducer` を使うとこんな感じになります。

```react:src/ComponentUseReducer.js
import React, { useReducer } from 'react'

const initialState = {num: 0};

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

const ComponentUseReducer = () => {
  const [state, dispatch] = useReducer(reducer, initialState)

  const { num } = state
  return (
    <div>
      <h2>Using useReducer</h2>
      Number: {num}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </div>
  );
};

export default ComponentUseReducer;
```

`useState` を使うより、コードが長くなってしまいますが、 `useReducer` を使うことによって、 足し算、引き算のロジックの部分は、`reducer` 関数の中に閉じ込めて、コンポーネントの `ComponentUseReducer` とは、分離することができます。
`reducer` は、純粋な JavaScript のコードなので、単体で実行することができ、テストもしやすそうです。（本来、reducer は、コンポーネントとは別ファイルに分割すべきです。ここでは、敢えて分割しておりません。）

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

例えば、以下のようにChrome のデベロッパーツールの console でも単独で実行できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209941/d3ef00fd-f430-6d21-67db-a763e416b6bb.png)

コードの中で、初期状態を示しているのが `initialState` です。この状態をボタンが押されるなどのイベントによってどう変化させていくのかというロジックの部分が　reducer になります。ですから、この **reducer の部分と initialState をどうするかは実際の開発では自分で考えて実装しないといけない**ところですね。（私はこのことに気づく（理解する）のに、随分と、時間がかかりました。）

ただ、 `reducer` 関数の書き方には、決まり事があります。

1. 引数は、state と action の２つ。
2. `action` は、 `type` で、state をどう変更させるかを指定する。
3. 戻り値は、変更後の新しい state にする。

ということで、コンポーネントとロジックを分けることができたのですが、やっぱり、 `num` を複数コンポーネントで共有するのはちょっと面倒そうです。

# React-Redux の useSelector と useDispatch を使う。 connect は使わない。

そこで、 Redux と Hooks 時代の React-Redux です。
Redux と React-Redux を使うとこんな感じになります。

```react:src/ComponentUseReactRedux.js
import React from "react";
import { createStore } from "redux";
import { Provider, useSelector, useDispatch } from "react-redux";

const initialState = { num: 0 };

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

const store = createStore(reducer, initialState);

const ComponentUseReactRedux = () => {
  return (
    <div>
      <h2>ComponentUseReactRedux</h2>
      <Provider store={store}>
        <ChildComponentUseReactRedux />
      </Provider>
    </div>
  )
}

const ChildComponentUseReactRedux = () => {
  const num = useSelector(state => state.num);
  const dispatch = useDispatch();
  return (
    <div>
      <h3>Using useSelector, useDispatch</h3>
      Number: {num}
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </div>
  );
};

export default ComponentUseReactRedux;
```

随分と長くなってしまいました :sweat_smile: 

`initialState` と `reducer` は、 `useReducer` を使った例 `src/ComponentUseReducer.js` と同じです。初期値とロジックなので、ここは変わりません。

```react
const store = createStore(reducer, initialState);
```

で、情報の格納先を１つ作ります。イメージ的には、これを複数のコンポーネントから参照する感じです。これはお約束の１つです。

`ComponentUseReactRedux` では、 React-Redux で提供される `Provider` を使って、 以下のように `ComponentUseReactRedux` の子コンポーネントからも、 `store` の情報にアクセスできるようにします。これもお約束ごとです。

```react
      <Provider store={store}>
        <ChildComponentUseReactRedux />
      </Provider>
```

子コンポーネントの `ChildComponentUseReactRedux` の中では、 `useSelector` を使って `num` の値を参照できるようにします。 

```react
  const num = useSelector(state => state.num);
````

ここで、登場する、 state は、 reducer 関数の引数の state と同じものです。以下のように書き換えた方がわかりやすいかも知れません。

```react
const selector = state => {
  return state.num;
}

const num = useSelector(selector);
```

また、 `useDispatch()` を使って、 `dispatch` を取得します。

```react
  const dispatch = useDispatch()
```

`ChildComponentUseReactRedux` の残りの部分は、 `useReducer` を使った場合と同じですね。

で、この書き方だと、複数のコンポーネントから、同じ `num` を参照できるのです。

試しに、 `ComponentUseReactRedux` の中の `ChildComponentUseReactRedux` を２つにしてみます。

```react
const ComponentUseReactRedux = () => {
  return (
    <div>
      <h2>ComponentUseReactRedux</h2>
      <Provider store={store}>
        <ChildComponentUseReactRedux />
        <ChildComponentUseReactRedux />
      </Provider>
    </div>
  )
}
```

するとその２つの子コンポーネントの `num` の値は連動して変化します。

![gif1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209941/a4243135-5a41-480b-c449-05ec0fba4778.gif)

# まとめ

1.  `useState` や `useReducer` は 規模の大きな開発には使わない。
2.  初期ステータスをどう定義するか、また、 `reducer` によってその初期ステータスをどう変化させるか（ロジック）は自分で考えて実装しないといけない。
3.  `createStore` を使ってステータスの格納先を作成する。
4.  `Provider` を使ってコンポーネント間で、ステータスを共有。
5.  `useSelector` を使って、ステータスのどの値を参照するのか指定する。
6.  `useDispatch` を使って、 `dispatch` を取得する。
7.  `connect` は使わない。 (mapStateToProps とか mapDispatchToProps とか私には難しい...。）

# まだ整理できていないこと

- `useContext` を使う方法もありそうなのですが、良く調べてません。
- redux-thunk や redux-saga などの middleware の部分をどうするのが良いのか、まだ自分の中で整理ができていません。 (`useCallback` が使えそうですが...)

# 追記

今回は、 dispatch の引数(Action)を素のまま書きましたが、この部分を関数化するのが、より正しいお作法なのかも知れません。

```javascript
const increment = () => {
  return {
    type: 'increment'
  };
}
const decrement = () => {
  return {
    type: 'decrement'
  };
}
```

を定義して、 dispatchを使うときに、 `dispatch(increment())` や `dispatch(decrement())` のようにします。