Redux 基礎：Action 編


# 概要

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

# 準備

```redux/examples/real-world``` からいろいろそぎ落とした[こちらのソース](https://github.com/yasuhiro-okada-aktsk/sample_redux/tree/init)の ```tag/init``` を使って説明します。
※ 実装完了は[```tag/action```](https://github.com/yasuhiro-okada-aktsk/sample_redux/tree/action)

# Action とは

Action はストアの state を変更するためのメッセージです。
Action によって state が変わると UI の表示などアプリの変化が起きるので、アプリに何か起こすための出発点となります。

1. Action 発行
2. Reducer が現在の state と action を元に新しい state を作成。
3. state の変更をUIなどに反映

Action は javascipt のオブジェクトでどのような形でも大丈夫ですが、慣習として ```type``` フィールドに文字列で Action のタイプを指定します。 ```type``` フィールド以外は自由に定義してください。

```js:例
{
    type: "SHOW_ERROR",
    error: message
}
```

# Action の作成

今回のサンプルには ```errorMessage``` という Reducer があります。

```js:reducer/index.js
function errorMessage(state = null, action) {
  const { type, error } = action;

  if (type === ActionTypes.RESET_ERROR_MESSAGE) {
    return null;
  }

  if (error) {
    return error;
  }

  return state;
}
```

この Reducer は ```action.type``` が ```RESET_ERROR_MESSAGE``` のとき、新しい状態として null を返し、```action``` に ```error``` があるとき 新しい状態として ```error``` を返します。
アプリはこの状態を UI にエラーメッセージとして表示します。

この Reducer が処理できる Action を発行します。

まず、Action を発行するには ActionCreator を作ります。ActionCreator は Action を返すだけの単純な関数です。
```actions/index.js``` にすでに ```resetErrorMessage``` という ActionCreator があるので同じように ```showErrorMessage``` を作ります。

```js:actions/index.js
export const SHOW_ERROR_MESSAGE = 'SHOW_ERROR_MESSAGE';

export function showErrorMessage(message) {
  return {
    type: SHOW_ERROR_MESSAGE,
    error: message
  }
}
```

# Action の発行

Action を発行するには、```store.dipatch(action)``` に Action を渡しますが、react-redux の [```connect```](https://github.com/rackt/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) を使うと ActionCreator (と同じ形の function) で自動的に発行できます。

```containers/App.js``` にすでに ```resetErrorMessage``` のコードがあるので、同じように ```connect``` に ```showErrorMessage``` を追加します。

```js:containers/App.js
import { resetErrorMessage, showErrorMessage } from '../actions';

export default connect(mapStateToProps, {
  resetErrorMessage,
  showErrorMessage,
  pushState
})(App);
```

```connect``` が props に showErrorMessage （と同じ形の function）をマッピングするため、その function を呼んで発行します。

```js:containers/App.js
  handleShowError(e) {
    this.props.showErrorMessage("sample error!!");
    e.preventDefault();
  }

  renderSample() {
    return (
      <ul>
        <li><a href="#" onClick={::this.handleShowError}>show error</a></li>
      </ul>
    );
  }
```

show error のリンクをクリックするとエラーメッセージが表示されます。 エラー表示後の Dissmiss をクリックするとエラーメッセージを消します。
右のペーンで Action と state の変化が確認できます。

# bindActionCreators

Action を追加するたびに connect に追加するのはたいへんなので、[bindActionCreators](http://redux.js.org/docs/api/bindActionCreators.html) を使って自動的にマッピングします。

```connect``` を次のように変更します。

```js:containers/App.js
import { bindActionCreators } from 'redux';
// import { resetErrorMessage, showErrorMessage } from '../actions'; 　// 削除
import * as Actions from '../actions';

function mapDispatchToProps(dispatch) {
  return {
    ...bindActionCreators(Actions, dispatch),
    pushState
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(App);

```

# 非同期の Action

１秒後に何かしたいなど、非同期で Action を実行したい場合は、ActionCreator で ```function (dipatch)``` 型の function を返します。

```js:actions/index.js

export function showErrorMessageDelayed(message, delay = 1000) {
  return dispatch => {
    setTimeout(() => {
      dispatch(showErrorMessage(message));
    }, delay);
  };
}
```

さらに store に [thunk](https://github.com/gaearon/redux-thunk) という middleware を追加します。

```js:store/configureStore.dev.js
import thunk from 'redux-thunk';

const finalCreateStore = compose(
  applyMiddleware(thunk),
  reduxReactRouter({ routes, createHistory }),
  applyMiddleware(createLogger()),
  DevTools.instrument()
)(createStore);
```

Action の発行や Reducer は特別な処理はなく同期型と同じです。

```js:containers/App.js
  handleShowErrorDelayed(e) {
    this.props.showErrorMessageDelayed("delayed sample error!!");
    e.preventDefault();
  }
```

# 最後に

サンプルで遊んでいるときは簡単だと思いましたが、Action がきちんと通るようにするまでが結構たいへんでした。
一度設定してしまえば、あまり変更しない部分なので自分用のテンプレートができると楽になると思います。

# 参考

* [Actions | Redux](http://redux.js.org/docs/basics/Actions.html)
* [Async Actions | Redux](http://redux.js.org/docs/advanced/AsyncActions.html)
* [reactjs - react+reduxで非同期処理を含むtodoアプリを作ってみる - Qiita](http://qiita.com/halhide/items/a45c7a1d5f949596e17d)