実践駆動解説React+Redux

React+Reduxで何かを作ろうとしたが、実装の仕方がよくわからない人向けの記事。
現在進行形で何かを作ってる人、そもそも理解している人にはあまり役に立ちません。
React+Reduxにおける実装において、それぞれの概念を一つ一つ分かりやすく解説する試みです。
**※どちらかというとReduxの解説になるのでReactの作法については触れません。**

# 環境を用意する

gitとnpmがインストールされていればすぐにできます。
本家Reduxから好きなサンプルを選んで動かしてみましょう。
https://github.com/reactjs/redux/blob/master/docs/introduction/Examples.md
以下に解説するコードは上記のサンプルでいうと`src`ディレクトリ配下のファイルにあたります。
中のコードを置き換えれば、当記事のコードもすぐに動かして確認することができます。

# エントリーポイントとコンポーネント

まずは、お決まりの**Hello world**を作ってみましょう。

## 表示用のDOM(HTML)を記述する

```js:components/Hello.js
import React from 'react'

let divStyle = {
  backgroundColor: 'mintcream'
}

const Hello = () => (
  <div style={ divStyle }>
    <h1>Hello, world.</h1>
  </div>
)

export default Hello
```
`components`というディレクトリ配下にあるコードはコンポーネントと呼ばれ、MVCにおけるViewの要素にあたります。

## エントリーポイントを作成する

作成したコンポーネントをReact+Reduxに埋め込みます。

```js:index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import Hello from './components/Hello'

render(
  <Provider>
    <Hello />
  </Provider>,
  document.getElementById('root')
)
```
`Provider`がReactとReduxをつなげたフレームワークとなります。
その中にさきほど作ったコンポーネント(`Hello`)を配置します。

以下のように出力されます。
<img width="233" alt="Redux_Counter_Example.png" src="https://qiita-image-store.s3.amazonaws.com/0/157051/5929371b-50d3-ab1b-8439-0a2e78ce9e01.png">

## ここまでのファイル構成

```
src
├── components
│   └── Hello.js
└── index.js
```

## ここまでの概念図
![test1.png](https://qiita-image-store.s3.amazonaws.com/0/157051/bb015627-e0aa-2e6f-0035-224d8a9f2111.png)

# ストアとリデューサー

上記の実装だと```Warning: Failed prop type: The prop `store` is marked as required in `Provider`, but its value is `undefined`.```という警告が出ます。
`Provider`には`store`が必要で、`store`には`reducer`が必要なので、次にこの２つを用意します。
簡単に説明すると、`store`は`state`を入れる箱で`reducer`は`state`を吐き出す装置です。
少々乱暴な例えですが、`state`はWebアプリケーションにおけるDBのようなものです。
フレームワークの構造上、write権限はリデューサーにしかなく、read権限はコンテナにしかありません。

## reducerの作成

まだ中身は何も作りませんが、[非公式のFluxコーディング規約](https://github.com/acdlite/flux-standard-action)に合わせるため、`redux-actions`を利用します。
package.jsonの`dependencies`に`redux-actions`を追加して`npm install`しておきましょう。

```js:reducers/bar.js
import { handleActions } from 'redux-actions'

const bar = handleActions({}, 0)
                                                                                                                                                                                                            
export default bar
```
上記の実装だとこのreducerは0が入った`state`を返却します。
すなわち、**reducerの戻り値がstateの構造を決めます。**

## storeの作成

Providerにstoreを登録します。

```js:index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import Hello from './components/Hello'
import bar from './reducers/bar'

let store = createStore(bar)

render(
  <Provider store={store}>
    <Hello />
  </Provider>,
  document.getElementById('root')
)
```
これで警告は消えました。
`store`は`state`を保持しており、この`state`には0が入っています。


## ここまでのファイル構成

```
src
├── components
│   └── Hello.js
├── index.js
└── reducers
    └── bar.js
```

## ここまでの概念図
![test2 (1).png](https://qiita-image-store.s3.amazonaws.com/0/157051/55b9b92d-eb37-30ac-77b1-0d6fc6f69e15.png)


# アクションとコンテナ

最後にアクションを発生させてViewの更新を行います。
これで基本的な概念は出揃います。

## アクションの作成

アクションはゲームコントローラのボタンを定義するようなものです。
この定義がないとそもそもゲーム内のキャラを動かすことができません。

```js:actions/index.js
import { createAction } from "redux-actions";

export const clickActionCI = createAction('CIRCLE')
export const clickActionT = createAction('TRIANGLE')
export const clickActionS = createAction('SQUARE')
export const clickActionCR = createAction('CROSS')
```
`createAction()`でPSコントローラの各ボタンを設計したと仮定してください。

## リデューサーにアクションごとの新しいstateの返却内容を実装する

上記で定義したアクションが発生した場合、**新しい`state`**([もとの`state`をいじってはいけない](http://qiita.com/pullphone/items/d28baeb296666a4847b8#memo-%E3%81%AA%E3%81%9C%E7%9B%B4%E6%8E%A5%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E3%81%A1%E3%82%83%E3%83%80%E3%83%A1))をどのような内容にするか、を記述します。

```js:reducers/bar.js
import { handleActions } from 'redux-actions'

const bar = handleActions({
  CIRCLE: (state, action) => {
    return 'decide'
  },
  TRIANGLE: (state, action) => {
    return state + 't'
  },
  SQUARE: (state, action) => {
    return state + 's'
  },
  CROSS: (state, action) => {
    return state.substr(0, state.length - 1)
  }
}, 'default')

export default bar
```
次のような実装にしてみました。

- 「default」を初期文字列とします。
- 丸ボタンの入力を検知すると「decide」を返却します。
- 三角ボタンの入力を検知すると現在の`state`の文字列の後ろにtを追加します。
- 四角ボタンの入力を検知すると現在の`state`の文字列の後ろにsを追加します。
- バツボタンの入力を検知すると現在の`state`の文字列の後ろ1文字を削ります。

## コンポーネントにボタンを追加

アクションを発生させるオブジェクトとしてボタンを作っておきます。
またボタンごとにonclickイベントを仕込み、クリックされた際に呼ばれる関数のインターフェースをPropTypesに登録しておきます。
あと、`state`を表示するためのオブジェクトを`Hello, world.`の間に仕込んでおきます。

```js:components/Hello.js
import React, { PropTypes } from 'react'

let divStyle = {
  backgroundColor: 'mintcream'
}

const Hello = ({ moji, clickTriangle, clickSquare, clickCircle, clickCross }) => (
  <div style={ divStyle }>
    <h1>Hello, {moji} world.</h1>
    <table>
      <tbody>
        <tr>
          <td></td>
          <td>
            <button onClick={ clickTriangle }>△</button>
          </td>
          <td></td>
        </tr>
        <tr>
          <td>
            <button onClick={ clickSquare }>□</button>
          </td>
          <td></td>
          <td>
            <button onClick={ clickCircle }>○</button>
          </td>
        </tr>
        <tr>
          <td></td>
          <td>
            <button onClick={ clickCross }>×</button>
          </td>
          <td></td>
        </tr>
      </tbody>
    </table>
  </div>
)

Hello.propTypes = {
  moji: PropTypes.string,
  clickTriangle: PropTypes.func.isRequired,
  clickSquare: PropTypes.func.isRequired,
  clickCircle: PropTypes.func.isRequired,
  clickCross: PropTypes.func.isRequired
}

export default Hello
```

## コンテナの作成

`state`の値と各アクションをコンポーネントに関連付けます。

```js:containers/Ps.js
import { connect } from 'react-redux'
import { clickActionCI, clickActionT, clickActionS, clickActionCR } from '../actions'
import Hello from '../components/Hello'

const mapStateToProps = (state) => {
  return {
    moji: state
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    clickTriangle: () => dispatch(clickActionT()),
    clickSquare: () => dispatch(clickActionS()),
    clickCircle: () => dispatch(clickActionCI()),
    clickCross: () => dispatch(clickActionCR())
  }
}

const Ps = connect(
  mapStateToProps,
  mapDispatchToProps
)(Hello)

export default Ps
```

コンポーネントの`propTypes`の`moji`は`mapStateToProps()`の`moji`と
コンポーネントの`propTypes`の各関数は`mapDispatchToProps()`のそれぞれのキー名と関連づいています。
これで

```

component(ボタン) --> container --> action --> reducer --> state --> container --> component(文字)

```
という流れができました。

## コンテナを配置

最初はPrividerの中にコンポーネントを直接置いていましたが、コンテナに置き換えます。
また、stateの中身がわかるように`redux-logger`も追加しています。(事前にpackage.jsonの`dependencies`に`redux-logger`を追加して`npm install`する必要あり)


```js:index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { applyMiddleware, createStore } from 'redux'
import createLogger from 'redux-logger'
import Ps from './containers/Ps'
import bar from './reducers/bar'

let store = createStore(bar, applyMiddleware(createLogger()))

render(
  <Provider store={store}>
    <Ps />
  </Provider>,
  document.getElementById('root')
)
```

以上で一通り出来上がりました。
動かすと以下のような表示と挙動になります。
![566ef963400584578ab5764893ec7b38.gif](https://qiita-image-store.s3.amazonaws.com/0/157051/b12f6f10-f3e3-1dcd-c8b4-bef19a1a39ed.gif)

## ここまでのファイル構成

```
src
├── actions
│   └── index.js
├── components
│   └── Hello.js
├── containers
│   └── Ps.js
├── index.js
└── reducers
    └── bar.js
```

## ここまでの概念図
![test3.png](https://qiita-image-store.s3.amazonaws.com/0/157051/8da8d172-d328-7fb7-5568-0beb6194aa06.png)

`redux-actions`にあまり言及できませんでしたが、今回はここまでです。