React Reduxの全体像とデータフロー

## はじめに
ずっとReduxから逃げて他のstate管理ライブラリを使っていたんですが、ついに捕まったので調査しました。


## Reduxの全体像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502798/61155023-ba22-7bbe-88bf-de4e5b767b22.png)

|  要素  |  役割  |
| ---- | ---- |
|  Component  |  Storeを利用するコンポーネント。ComponentはUIにのみ責任を持ち、ロジックはContainerに分離する。 |
|  Action  |  Storeに対して、stateの参照や変更を依頼するための依頼書。ComponentがActionCreatorを通して作成する。  |
|  ActionCreator  |  Componentから必要な情報を受け取り、Actionを作成する  |
|  Container  |  ComponentとStoreを接続し、Componentに対してstateとdispatch()を提供する。stateに対して何がしかの処理を加えたい場合、Container内にロジックを記述する。  |
|  dispatch()  |  actionをReducerに届け、処理を促す。  |
|  Store  |  データストア。stateを集中管理する。Reducer以外からのstate変更を受け付けない。  |
|  Reducer  |  Actionを受け取り、Store内のstateに変更を及ぼす。stateの種類によってReducerを分けるのが一般的。 |
|  CombineReducer  |  複数あるReducerをひとまとめにし、Storeと接続する。  |


## ユーザアクションに応じて、Store内のstateを更新する際のデータフロー
下記流れでStore内のstateを更新します。

1. Componentがイベントを検知し、Actionを作成する。
1. 作成したActionを、Containerから提供されたdispatch()を用いて、Reducerに処理を依頼する。
1. Reducerは、現在のstateとActionをもとに新たなstateを計算し、そのstateをStoreに反映させる。

### Componentがイベントを検知し、Actionを作成する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502798/a94ba317-b37c-028e-13da-91811a9c7ffa.png)

Componentはイベント(ユーザのクリックとか)を検知して、ActionCreatorを通じてActionを作成します。

この時、ComponentはActionCreatorに対して、下記の情報を提供します。

|    |   |
| ---- | ---- |
|  Type  |  アクションの種類を識別するための文字列  |
|  Payload  | アクションに必要なデータ(引数)  |

ActionCreatorは上記をもとに、Actionを作成します。

Actionの例↓

```
{
    type: "todos/todoAdded",
    payload: todoText,
}
```

### 作成したActionを、Containerから提供されたdispatch()を用いて、Reducerに処理を依頼する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502798/228d65d0-91c2-59c2-2497-7718c725837a.png)

ComponentはActionをDispatchして、Reducerに処理を依頼する。

```index.js
dispatch({ type: 'todos/todoAdded', payload: 'todoText' })
```

Reducerの中身はこんな感じ↓

```todosReducer.js
// Use the initialState as a default value
export default function appReducer(state = initialState, action) {
  // The reducer normally looks at the action type field to decide what happens
  switch (action.type) {
    // Do something here based on the different types of actions
    case 'todos/todoAdded': {
      // We need to return a new state object
      return {
        // that has all the existing state data
        ...state,
        // but has a new array for the `todos` field
        todos: [
          // with all of the old todos
          ...state.todos,
          // and the new todo object
          {
            // Use an auto-incrementing numeric ID for this example
            id: nextTodoId(state.todos),
            text: action.payload,
            completed: false
          }
        ]
      }
    }
    default:
      // If this reducer doesn't recognize the action type, or doesn't
      // care about this specific action, return the existing state unchanged
      return state
  }
}
```

ActionのTypeを参照して、処理を分岐させてる。
また、Action.payloadを参照することで、Componentが引数として指定した任意のデータも参照することができる。

### Reducerは、現在のstateとActionをもとに新たなstateを計算し、そのstateをStoreに反映させる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502798/fd20be38-e9fd-e597-8902-5f1b197a8dac.png)

実際に新たなstateを反映しているのは、下記のreturn文。

```todosReducer.js
case 'todos/todoAdded': {
      // We need to return a new state object
      return {
        // that has all the existing state data
        ...state,
        // but has a new array for the `todos` field
        todos: [
          // with all of the old todos
          ...state.todos,
          // and the new todo object
          {
            // Use an auto-incrementing numeric ID for this example
            id: nextTodoId(state.todos),
            text: action.payload,
            completed: false
          }
        ]
```

## 補足）ContainerとComponent

Containerの役割はStoreとComponentの橋渡し。
しかし、ComponentはContainerがいなくても、直接Storeのdispatch()を利用することができる。

### なぜContainerが必要か？

ComponentがReduxに依存してしまう。
⇒

- Componentが状態やロジックを保持することになり、テストがしにくくなる。
- Redux以外のデータソースから、Componentに対して値を受け渡しにくくなる。

ちなみに、Containerは下記のように、Componentに対してStateとdispatch()を提供する。

```container.js
function mapStateToProps (state) {
  return {...state}; 
}

function mapDispatchToProps(dispatch) {
  return {
    todoAdded: () => dispatch(todoAdded()),
  };
}


export default connect(mapStateToProps, mapDispatchToProps)(Component);
```

※上記のconnect()は古いらしく、hook全盛期のReactではもっとシンプルに書けるそうです。
https://qiita.com/seya/items/700184c0d4a52bc0d32b

間違いあればご指摘ください！

## 追記
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502798/21998f3a-8f2d-e3e7-f187-8113e03bac3a.png)

Container内で、dispatch()をカスタマイズ(任意のActionを定義)する関数を作成し、Componentはその関数に対してPayloadを投げてあげる。みたいな構成もあるらしい。

(そっちの方がAction単位でテストできるし、Containerの肥大化も避けれるしよさげ？？)


## 参考
https://redux.js.org/tutorials/essentials/part-1-overview-concepts
https://qiita.com/mpyw/items/a816c6380219b1d5a3bf#action-%E3%81%8A%E3%82%88%E3%81%B3-action-creator
https://qiita.com/seya/items/700184c0d4a52bc0d32b
https://react-redux.js.org/api/hooks