コードで理解するRedux(React使用)

#対象読者
- Reactの書き方がある程度はわかってる人
- Reduxの概念とか言われてもわからない、コードでくれって人
- 私（備忘録）

#Reduxとは
fluxの考え方にもとづいて作られたフレームワーク
React専用ではないが、Reactと連携させることでより高いパフォーマンスを得られる

##【参考】fluxとは
facebookが提唱したアーキテクチャ（ソフトウェア設計の思想・考え方）
データの流れを一方通行にしたもの
![flux流れ.png](https://qiita-image-store.s3.amazonaws.com/0/156086/a3c1e6fb-5cdf-8a40-cdf6-9dfe4b11e968.png)

##Reduxの特徴
Reduxの3原則
**１．Single source of truth**
　１つのアプリケーションが持つstateは１つのみ
　stateをオブジェクトツリー構造で管理する

**２．State is read-only**
　stateを直接変更することはできない
　変更したいときはActionをdispatchで渡したときのみ

　これにより、stateの変更に関わる部分が限定化され、バグの特定が容易になる

**３．Changes are made with pure functions**
　reducerは純粋関数である
　同じ引数を渡されたら同じデータを返す

　ランダム値や現在の日付をReducer内で取得しない



#サンプル
##プログラム概要
入力した内容の税込(8%)の金額を表示させるだけのプログラム

![aaa.gif](https://qiita-image-store.s3.amazonaws.com/0/156086/28d7d93b-d0cb-03c1-4fe2-ae78f0c213eb.gif)

##reduxのイベント発生後の流れ
![2016-12-26_115934.png](https://qiita-image-store.s3.amazonaws.com/0/156086/784e5746-fba3-e8e1-60c2-3d248f988529.png)


##View
![2016-12-26_120430.png](https://qiita-image-store.s3.amazonaws.com/0/156086/086826ec-b522-69a1-c203-a7fae66b02ea.png)
表示させる部分で、イベントの発火位置でもある
* * *
###Presentational Components
いままでのReactコンポーネントのこと

```jsx:ボタンクリックで、Inputに入力した値を引数としてpropsとして受け取ったonClick関数を実行する
class AppComponents extends React.Component {

  send(e){
    this.props.onClick(this.refs.inputText.value);
  }


  render() {
    return (
      <div>
        <input type="text" defaultValue="" ref="inputText" /> { /* 入力フォーム */ }
        <button onClick={this.send.bind(this)}>計算</button>  { /* ボタン */ }
        <br />
        {this.props.price}  { /* 表示させる税込の金額 */ }
      </div>
    );
  }
}
```
* * *
###Container Components
Reduxと連携するコンポーネント
正直ここが一番ややこしい

```js:PresentationalComponentsにpriceのStateとonClickの関数を渡す
function mapStateToProps(state) {
  return {
    price: state.price
  };
}

function mapDispatchToProps(dispatch) {
  return {
    onClick(price){
      dispatch(addTax(price));
    }
  };
}

let AppContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(AppComponents);
```

####mapStateToProps
Presentational Componentsに渡すStateとして`price`を指定している

####mapDispatchToProps
Presentational Componentsに渡す関数として`onClick`を指定している
`addTax(price)`はActionCreator（後述）の関数でAction（オブジェクト）が返ってくる
`dispatch(Action)`を実行することでReducer（後述）が実行される

また、`bindActionCreators`を使用することで、ActionCreatorの関数をdispatchせずに実行できる

```js:bindActionCreatorsの例
import * as Actions from './../actions/app';

const mapDispatchToProps = (dispatch) => (bindActionCreators(Actions, dispatch));
```

####connect
React-ReduxのconnectメソッドでReactとReduxをつなげている

1つ目の引数に**[Stateを返す関数]**と**[Stateを変更する関数を書いた関数]**を、
2つ目の引数に**[Presentational Componentsのトップのコンポーネント]**を指定する

上記のように書くことでPresentational Componentsのトップのコンポーネントで`this.props.price`や'this.props.onClick'のように使用できるようになる

##ActionCreator
![2016-12-22_182655.png](https://qiita-image-store.s3.amazonaws.com/0/156086/926addd5-263e-1346-020d-50b9c237ef6d.png)

どのアクションなのかをReducerに判別させるため、typeという要素をつける
typeはconstで定数化するのがツウらしい

```js:受け取ったpriceをActionにして返す関数
const ADDTAX = 'ADDTAX';
function addTax(price) {
  return {
    type: ADDTAX,
    price
  };
}
```
##Reducer
![2016-12-22_182622.png](https://qiita-image-store.s3.amazonaws.com/0/156086/16681bc2-dcbb-8952-3dcd-1bbd6836b8c4.png)


Actionで指定したtype要素をswitch文で判別し、関数を実行する
引数にStateとActionを指定する

```js:Actionのpriceを1.08倍して現在のStateにマージし、新しいStateとして返す関数
function appReducer(state, action) {
  switch (action.type) {
    case 'ADDTAX':
      return (
        Object.assign({}, state, {price: action.price * 1.08})
      );
    default:
      return state
  }
}
```

##初期処理とレンダリング
```jsx:storeを作成し、レンダリングする
//state初期化
const initialState = {
  price: ''
};

//store作成
const store = createStore(appReducer, initialState);

//レンダリング
ReactDOM.render(
  <Provider store={store}>
    <AppContainer />
  </Provider>,
  document.getElementById('root')
);
```
###createStore
ReducerとState初期値を渡してStoreを作成する
そのStoreをView（Container Components）に渡すことでReactでstoreを使える

Reducerを複数に分けたい場合は`combineReducers`を使う

```js:combineReducersの例
const store = createStore(
  combineReducer(FirstReducer, SecondReducer)
)
```

###Provider
storeプロパティに先程作ったstoreを入れる
Providerコンポーネントの子コンポーネントとしてView（Container Components）を指定する

##コード全体
実行にはreduxとreact-reduxのインストールが必要

```sh
npm install --save redux
npm install --save react-redux
```

```jsx:
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'

//Presentational Components
class AppComponents extends React.Component {

  send(e){
    this.props.onClick(this.refs.inputText.value);
  }


  render() {
    return (
      <div>
        <input type="text" defaultValue="" ref="inputText" /> { /* 入力フォーム */ }
        <button onClick={this.send.bind(this)}>計算</button>  { /* ボタン */ }
        <br />
        {this.props.price}  { /* 表示させる税込の金額 */ }
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
    price: state.price
  };
}

function mapDispatchToProps(dispatch) {
  return {
    onClick(price){
      dispatch(addTax(price));
    }
  };
}

let AppContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(AppComponents);



// ActionCreator
const ADDTAX = 'ADDTAX';
function addTax(price) {
  return {
    type: ADDTAX,
    price
  };
}


// Reducer
function appReducer(state, action) {
  switch (action.type) {
    case 'ADDTAX':
      return (
        Object.assign({}, state, {price: action.price * 1.08})
      );
    default:
      return state
  }
}


//state初期化
const initialState = {
  price: ''
};

//store作成
const store = createStore(appReducer, initialState);

//レンダリング
ReactDOM.render(
  <Provider store={store}>
    <AppContainer />
  </Provider>,
  document.getElementById('root')
);
```

##コードで見るイベント発生後の流れ
![2016-12-26_115934.png](https://qiita-image-store.s3.amazonaws.com/0/156086/784e5746-fba3-e8e1-60c2-3d248f988529.png)
上にも貼ったこの画像で処理の流れをコードで見ていきます。

###①【計算】ボタンをクリックしたときContainer Componentsから受け取った関数を実行する
![2016-12-26_124731.png](https://qiita-image-store.s3.amazonaws.com/0/156086/19f5f11a-4c1e-ec25-e5cd-04aa6e59cdca.png)
###②ActionCreatorの関数を実行する
![2016-12-26_125158.png](https://qiita-image-store.s3.amazonaws.com/0/156086/5e118362-3dd6-8e1b-269d-e5a023424393.png)
###③入力金額が入ったAction（オブジェクト）を返す
![2016-12-22_174410.png](https://qiita-image-store.s3.amazonaws.com/0/156086/34f77428-c82a-7dda-391d-147badf3c399.png)
###④dispatch(Action)を実行する 実行すると勝手にReducerが動く
![2016-12-22_174710.png](https://qiita-image-store.s3.amazonaws.com/0/156086/211e01c7-068f-2188-7b30-5d61bb20409e.png)
###⑤⑥新しいStateになったのでViewに渡している値も変わる
![2016-12-26_130916.png](https://qiita-image-store.s3.amazonaws.com/0/156086/15b3db2c-1987-c4fd-57ea-a9044e8f8405.png)
#参考にしたサイト
http://mae.chab.in/archives/2885
http://qiita.com/kiita312/items/49a1f03445b19cf407b7
http://qiita.com/gcfuji/items/547ab425f96bf7134b9d