React Redux の難しかった点をできるだけシンプルに図解

## はじめに

**この記事はだいぶ古くなっており、現在は[React Hooksを使ったReduxとComponentの繋ぎこみ方法があるようです](https://react-redux.js.org/next/api/hooks)。この記事ではそのあたり触れられてないので注意です。（Reduxの基本的な記事としてはまだ使えると思います、、、思いたい。）**

React-Reduxを人に教える機会があるのですが、中々キャッチアップが難しいFWです。
自分自身もReact自体未経験のところから、それなりに規模のある開発に参画したので、
その時に「ここが難しかったなあ」というところを自分なりに図解してみたいと思います。


### 前提
- ReactとReduxの接続には[react-redux](https://github.com/reduxjs/react-redux)のconnectを使うこととします。

### この記事で伝えたいところ
自分が難しく感じたのはこのあたりでした。

- （ソースが色々あって）全体の流れがよくわからない  
- actionを起こすとreducerが反応する仕組みがよくわからない
- mapStateToPropsやmapDispatchToPropsがおまじないにしか見えない

読み終えたときにこれらが理解できることを目指して書いていきたいと思います。
なんとなく「こうすれば一通り動くのはわかるけど理由が知りたい」人向けです。


## Reduxだけの場合
まずReactを省略してReduxだけの概念を図解します。

Reduxとは、アプリケーションの状態(State)管理を容易にするためのフレームワークです。
次の図を見ると矢印の向きが1方向になっているのがわかります。
このように単一方向の状態管理を実装するためのStoreという層を提供してくれます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/283253/5ee10a5e-e9c8-dd21-0225-30cf28abc1f3.png)

全体の流れは以下です。（図中の数字と対応付けてます）

1. View(画面)のInputをもとに、Action Creator(*)を使って`action`を生成する。
2. Storeに対してactionを`Dispatch`する。
3. ReducerがDispatchに反応して、actionをもとに`State`を更新する。
4. ViewはStoreから`State`を参照する。（getStateと言う取得関数があります。）

これの繰り返しで状態を更新・管理していきます。

(*) actionはViewで直接生成したりしてもよいのですが、大抵Action Creatorが`actions`等のjsにまとめて定義されます。こんなやつです。

```javascript
// create addTodo action
function addTodo(text) { 
    return { 
        type: ADD_TODO, 
        text
    } 
}
```
(このaction creatorとactionがごっちゃになりがちなのも個人的に最初難しく感じるところでした)

図中のMiddlewareについては今は触れないでおきます。

- Stateの更新はReducerが行う。
- Reducerに働いてもらうためには、ActionをStoreにDispatchする。
- Stateを取得するには、StoreからgetStateする。

という点を抑えておきます。

## React-Reduxの場合
次に、実際にReduxをReactで使う場合です。

### ReactとReduxを繋ぐContainer
ReactとReduxを繋ぐComponentをContainer Componentと呼びます。
以下のようなイメージになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/283253/b0bb9e00-2166-40ae-c897-ebe5fb42de32.png)

1. Container Componentは`this.props.anyAction()` のようにしてactionを発行できるようになります。
2. 同様に、`this.props.anyState`のようにしてStateを参照できるようになります。

前後しますが、コード例としては以下のようになります。

```javascript
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'

class MyContainer extends Component {
  // 省略
}

const mapStateToProps = (state, ownProps) => {
  return {
    todos: state.todos
  }
}

const mapDispatchToProps = { // あえて関数ではなくオブジェクトにしておきます。
    addTodo
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(MyContainer)
```

この時のmapStateToPropsとmapDispatchToPropsを
理解できていればreact-reduxについてはバッチリだと思います。

### mapStateToPropsとmapDispatchToProps

「Reduxだけの場合」に記載した以下のポイントを思い出しましょう。
> - Reducerに働いてもらうためには、ActionをStoreにDispatchする。
> - Stateを取得するには、StoreからgetStateする。

`connect(mapStateToProps, mapDispatchToProps)` は、
Reduxの`Store`をReact Componentの世界に持ち込んでくれます。

以下のようなイメージです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/283253/ac751b3e-f440-79c4-b5c0-64a263073d07.png)

1. mapStateToPropsは、Store.getState()のような役割をして、ComponentのpropsにStateの中身を詰め込んでくれます。
2. mapDispatchToPropsは、ActionCreatorをラップした、`actionをStoreにDispatchしてくれる関数`をpropsに詰め込んでくれます。

特に2点目はたまに理解できてない場合があります。
たとえば、上の方にあるコードで、importしているaddTodo()をpropsを通さず直接実行してもReducerは反応しません。
それは、単純にActionCreatorだけを実行しても、Dispatchしたことにならないからです。

mapDispatchToPropsは関数でも書くことができて、そちらを見るとわかりやすいです。
（自分が今まで見たコードではいつも単純なオブジェクトでした。。）

```javascript
const mapDispatchToProps = dispatch => ({
  // actionCreatorで生成したactionをdispatchする関数を作っている。
  // これがMyContainerに渡すaddTodoになる。
  addTodo: text => dispatch(addTodo(text)) 
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(MyContainer)
```

このような形で、Storeへのアクセスを私たちが意識しなくていいように
React Componentへ、文字通り繋いでくれるのが`connect(mapStateToProps, mapDispatchToProps)`関数になります。
※connect関連のapiには、例には書いてない引数や機能もあるので興味ある方は[react-reduxのGitHub](https://github.com/reduxjs/react-redux)を読んでみてください！



そして、すべてのReact ComponentをContainer化してしまうと
Reduxへ依存し過ぎてしまい再利用性のあるComponentが書けなくなってしまうため、
大抵以下のようにpropsを使って、子のComponentへ関数や値を渡して利用します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/283253/11635b4d-384c-adfc-fd4b-7443bc0f9c85.png)

### Provider
でも、ここまでのコード例で一度も`Store`なんて出てきてないのに
どうやってStoreを利用できるの？と思うかもしれません。そういった疑問を持てるのは素敵です。

[react-reduxからのほぼ引用](https://github.com/reduxjs/react-redux/blob/master/docs/api.md#provider-store)になりますが、下記のように`Provider`を使うことで
ラップされたComponent達がStoreとconnectできるようになります。

```javascript
// 主旨じゃないimportとかは省略してます

import { Provider } from 'react-redux'

const store = createStore(myRootReducer)
​
ReactDOM.render(
  <Provider store={store}>
    <MyRootComponent/>
  </Provider>,
  document.getElementById('root')
)
```

ここまで知っていればReduxをReactで使う方法はほぼ全てわかったと言って良いと思います。お疲れさまでした。

実際にはreact-routerと一緒に使うことも多いと思います。[real-world example](https://github.com/reduxjs/redux/blob/master/examples/real-world/src/containers/Root.dev.js)が参考になります。

## Middlewareについて

図中で放置されていたMiddlewareについてもさっと触れておきます。
Storeについて理解できていればあまり難しいことはありません。

- actionがReducerにたどり着く前に、actionを見て処理を実行できる場所
- Middlewareは、Storeにアクセスできる

この２点だけ抑えておけば大丈夫だと思います。

Reducerへ辿りつく前に、その場でactionを捨てたり、作り直したり、関心がない場合は何もしないこともできます。
Storeにアクセスできるので、今のStateを参照したり、その場で新たなactionをDispatchしたりできます。

例えば、react-redux公式の[real-world example](https://github.com/reduxjs/redux/blob/master/examples/real-world/src/middleware/api.js)では、
シンボル化された`CALL_API`actionを拾って、API実行の結果に応じて
request, success, failureに分けてactionをReducerへ流すようにしています。


## 終わりに

繰り返しになりますが、

- Reducerに働いてもらうためには、ActionをStoreにDispatchする。
- Stateを取得するには、StoreからgetStateする。

この点が理解できていればReduxを難しいと感じなくなるのかなと思います。
例えば、redux-thunkというライブラリがありますが、これもaction creatorの中で
Storeのdispatch、getStateができるだけと考えれば難しくありません。

うまく説明できたかわかりませんが、これからReact/Reduxの世界へ飛び込む人の助けになればと思います。
（そして将来また人に教えるときの自分の助けにもなるといいな。。。と思って書きました。）

以上です。