【上っ面だけでも】Redux【理解したい】

[コードで理解するRedux(React使用)](http://qiita.com/enshi/items/19b1924b72f8c2ffd1eb)
を理解するために、自分なりの解釈をまとめた記事です。

# Redux追加 環境作成
```bash
npm install --save-dev redux react-redux
```

# Redux三原則をもっと簡単にしてみる
**①コンポ単位でstate管理するのだりーよ！一個に集約しようぜ！**
Storeという管理者におまかせするようにした。

**②state触りたいならちゃんと手順踏んでね**
バグが特定しやすくなる。

**③reducerは同じデータなら毎回同じデータを返すこと**
すいません、ここだけ理由ができていません。

# 登場人物と役割
参考
[Redux + React (Example: Todo List) の構成図](http://qiita.com/yukisov/items/6aee862d2c62ed1313ff)
[Reduxでコンポーネントを再利用する](http://qiita.com/kuy/items/869aeb7b403ea7a8fd8a)

||説明|
|:--|:--|
|Store<br>(ストア)|stateの管理者。<br><br>アプリ内で一つしか存在しない。|
|Provider<br>(プロバイダ)|Storeと他コンポーネントの仲介役。<br><br>react-reduxが提供するコンポーネントで、アプリ内で一つしか存在しない。|
|Container<br>(コンテナ)|stateの変更通知を受け取って、propsを子に渡してあげるのが主な目的。<br><br>渡すpropsには、さり気なくdispatch(後述)を仕込むのがミソ。|
|Presentatinal<br>(プレゼンテーション)|描画特化。<br><br>①propsを使ってDOMの描画。<br>②ユーザから要求がアレば、propsとして受け取った関数の呼び出し。<br><br>ただコレだけを行う。|

# Reduxの流れを知る
## まず、なんとか理解してほしいこと
+ state(画面の状態)はStoreが管理する
+ stateの変更はStoreにある「**Reducer**」という場所で行う
+ Reducerはdipatchという行為で実行される(つまり、**dispatchする=Reducerの起動**)
+ dispatchをするにはActionと呼ばれる物が必要(処理分岐で使うだけです)
+ Reducerは新しいstateを用意するのでコンポーネントはそれを描画してあげる

## 今まで出てきた言葉を使って、Reduxをする
①画面でユーザーの要求が発生(stateの変更要求)
②Containerにあるイベントが実行
③イベントはReducerを起動するためにdispatchする(Actionの作成もここで)
④Reducerはstate(要求時の状態)とActionから、新しいstate(要求後の状態)をContainerに渡す
⑤Containerはstateをpropsに変換して、画面で描画する
⑥終わり

![Redux(1).jpg](https://qiita-image-store.s3.amazonaws.com/0/24484/5600d97a-32f2-7491-fff7-0ab9924d617b.jpeg)

# コード
ボタン押すたびにカウントアップするだけの処理。
役割ごとにファイルで分けてみました(コレがベストかは別にして)

## 処理の流れを踏まえた、実装までのコーディング方法
|目的|どこで|なにを|
|:--|:--|:--|
|React下準備|React描画部|ReactDOM.renderで指定できるところを用意。|
|Store作成|Store|createStore関数でStoreを作成。<br><br>引数で必要なReducerとstate初期化関数は、中身がなくてもいいので定義しちゃいましょう。|
|Providerを用意|Provider監視部|作成したStoreをProviderのプロパティにセットして、JSXに記載。|
|メインViewを用意|Presentatinal|中身はなくてもいいので、イベントハンドラ定義まで進める。|
|イベントハンドラでよぶ関数を準備|Container|stateが変わるなら、Reducerに仕事をお願いしないといけないので、関数内でdispatchの呼び出しを実装。|
|Actionの作成|Container|dispatchの呼び出しに必要なので、関数を定義。|
|Reducerの実装|Store|作成途中だったReducerに、Actionと既存のstateから、どう新しいstateを返すか定義。|
|新しいstateをpropsに変換|Container|関数実装。|
|コンテナのイベントを呼び出す|Presentatinal|イベントハンドラ内でContainerで定義したイベントを追記。|
|変換したpropsを用いて描画|Presentatinal|JSXに追記。|
|(state = props)の初期値に対応|Store|state初期化関数の中身実装。|
|PresentatinalとContainerの紐付け|Container|connect関数の実装。|
|コンテナをProviderの監視下に|Provider監視部|JSXにContainerを追記。|

## 実例

**React描画部**

```html:index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
```

**Provider監視部**

```js:index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import store from './Store.js';
import Container from './Container.js';

//①唯一のStoreを含むProviderが、コンポーネントを包むように記載
//②stateを切り離されたコンポーネントたちを下層で定義
ReactDOM.render(
    <Provider store={store}>
      <Container />
    </Provider>
    ,
    document.getElementById('root')
);
```

**Store**

```js:Store.js
import { createStore } from 'redux'

//Reducer
function reducer(state, action) {
  //Actionに応じて、処理分岐
  switch(action.type) {
    case 'ACT1':
      //新しいstateを作成
      //return先は、コンテナのprops変換処理かな？(未確認)
      return (
        Object.assign(
          {}                                  //空のオブジェクトに以下を追加
          , state                             //既存state
          , {count: Number(action.count) + 1} //別途変更がある箇所
        )
      );
    default:
      return state;
  }
}

//state初期化
const initialState = {
  count: 0
};

//Store定義(初期stateとReducerのセット)
export default createStore(reducer, initialState);
```

**Presentatinal**

```js:App.js
import React, { Component } from 'react';

// プレゼンテーション層
export default class App extends Component {
  //イベントハンドラ
  eClick(event) {
    //コンテナでdispatchする
    this.props.goDispatch(event.target.value);
  }
  render() {
    return (
      <div>
        {/* 押されるたびにカウントアップするボタン */}
        <button value={this.props.count} onClick={this.eClick.bind(this)}>ClickMe</button>
        <br />
        {this.props.count}
      </div>
    );
  }
}
```

**Container**

```js:Container.js
import { connect } from 'react-redux'
import App from './App.js';

//イベントをpropsで使うために定義
function addProps(dispatch) {
  return {
    //propsで参照できるイベントを定義
    goDispatch(count) {
      //stateが変更させたいので、イベント発火でReducerを起動させる
      dispatch(actOne(count));
    }
  };
}

//Action作成
function actOne(count) {
  return {
    type: 'ACT1', //Reducerで、「action.type」で参照
    count         //Reducerで、「action.count」で参照
  };
}

//state->props変換
function mapStateToProps(state) {
  return {
    count: state.count
  };
}

//プレゼンテーション層に、変換ルールとイベントハンドラを紐付け
export default connect(
  mapStateToProps
  ,addProps
)(App);
```