Redux 基礎：Reducer 編

# 概要

React + Redux のサンプルを使って、Reducer の作成と動作の確認をします。

# 準備

[Action 編](http://qiita.com/yasuhiro-okada-aktsk/items/09ab40af831c6d73491b)の続きに modal のクラスを追加したコード([```tag/init_reducer```](https://github.com/yasuhiro-okada-aktsk/sample_redux/tree/init_reducer))から行います。
※ 実装完了は[```tag/reducer```](https://github.com/yasuhiro-okada-aktsk/sample_redux/tree/reducer)

メッセージダイアログを表示する Reducer の作成を行います。

# Reducer とは

Reducer は現在の state と Action から、新しい state を生成します。

```js:Reducer
(previousState, action) => newState
```

Reducer 自体は function ですが、Reducer 名がそのまま state 名になります。

```js
function errorMessage(state = null, action) {
    :
  return newState;
}

state : {
  errorMessage: newState,
  otherStates: ...
}
```

Reducer は新しい state を生成してください。　前の state を変更してはいけません。

```js:newState
function reducer(state = null, action) {
  return Object.assign({}, 
      state, 
      {
        completed: true
      });
}
```

Action はすべての Reducer に送られるため、関係のない Action の場合は現在の state を返してください。

```js
function reducer(state = null, action) {
  if (action.type != ACTION_A) {
    return state;
  }
    :
  return newState;
}
```

# よく使うパターン

## 配列の追加

```js
[...state.values, newValue]
```

## 配列のある要素の変更

```js
[
 ...state.slice(0, action.index),
 modifiedValue,
 ...state.slice(action.index + 1)
]
```

## オブジェクトの変更

```js
Object.assign({}, 
  state, 
  {
    completed: true,
    name: "new name"
  });
```

# Reducer の作成

```MessageModal``` クラスは ```title```. ```message```, ```visibility``` を持った ```modal``` オブジェクトをとるので、表示するだけの Reducer を作ってみます。

```js:reducers/index.js
function messageModal(state = null, action) {
  return {
    visibility: "show",
    title: "Sample Modal",
    message: "Lorem ipsum dolor sit amet, consectetur adipisicing elit,"
  }
}
```

これを実行すると、何も起きませんが右のペーンを見ると、```messageModal``` state に ```title```. ```message```, ```visibility```　が設定されていることがわかります。

ダイアログが表示できるように、```containers/App.js``` を修正し state を props に設定します。

```containers/App.js
import MessageModal from "../components/MessageModal.js"

   :

  render() {
    const { children, inputValue, messageModal } = this.props;
    return (
      <div>
        {this.renderErrorMessage()}
        {this.renderSample()}

        <MessageModal modal={messageModal} onPrimaryButton={() => {}}/>
        {children}
      </div>
    );
  }

   :

function mapStateToProps(state) {
  return {
    messageModal: state.messageModal,
    errorMessage: state.errorMessage,
    inputValue: state.router.location.pathname.substring(1)
  };
}
```

実行するとメッセージダイアログが表示されます。

# Action による処理

Reducer が state を作ってダイアログが表示できるようになったので、今度は Action によって表示・非表示をコントロールできるようにします。

まず、Action を作成します。

```js:actions/index.js
export const SHOW_MODAL = "SHOW_MODAL";
export const HIDE_MODAL = "HIDE_MODAL";

export function showModal(title, message) {
  return {
    type: SHOW_MODAL,
    title: title,
    message: message
  }
}

export function hideModal() {
  return {
    type: HIDE_MODAL
  }
}
```

ダイアログはデフォルトでは非表示なので、初期値を次のようにします。

```js:reducers/index.js
const messageModalInitial = {
  visibility: "hide"
};

function messageModal(state = messageModalInitial, action) {
   :
}
```

```type``` が ```SHOW_MODAL```, ```HIDE_MODAL``` のときに新しい state を作ります。

```js:reducers/index.js
function messageModal(state = messageModalInitial, action) {
  const { type, title, message } = action;

  switch (type) {
    case ActionTypes.SHOW_MODAL:
      return {
        visibility: "show",
        title: title,
        message: message
      };

    case ActionTypes.HIDE_MODAL:
      return {
        visibility: "hide"
      };

    default:
      return state;
  }
```

関係のない ```action``` に対しては何も行わないので、```default``` では現在の ```state``` をそのまま返します。

ここで実行すると、初期状態が ```hide``` なのでダイアログが表示されなくなります。また、右のペーンで ```visibility``` が ```hide``` になっていることが確認できます。

ボタンを追加して、```showModal``` を呼びます。

```containers/App.js
    :

  handleShowModal(e) {
    const title = "Sample Modal";
    const message = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut
      labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
      aliquip ex ea commodo consequat.
      Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
      Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`;
    this.props.showModal(title, message);
    e.preventDefault();
  }

    :

        <button type="button" className="btn btn-primary btn-lg" onClick={::this.handleShowModal}>
          show modal
        </button>
```

show modal ボタンを押すと ```state``` が更新されダイアログが表示されます。

同じように close ボタンで ```hideModal``` が呼ばれるように修正します。

```containers/App.js
   :

  handleModalPrimaryButton(e) {
    this.props.hideModal();
    e.preventDefault();
  }

   :

  <MessageModal modal={messageModal} onPrimaryButton={::this.handleModalPrimaryButton}/>

   :
```

# 最後に

Reducer は state の形が決まっていれば実装は割と簡単だと思います。
ただ、Redux や flux では同じ state のときには同じ表示になることを意識する必要があり、UI の表示が決められるような state を設計するのが重要になります。

コンポーネントとかモジュールとかいうとメソッドが呼ばれてそ動くことが多かったので、state を中心にして state に従って UI に反映するというのは考え方を変えていかないといけないと感じました。

# 参考

* [Reducers | Redux](http://redux.js.org/docs/basics/Reducers.html)