Redux: Actionのコーディング規約 と redux-actions

# 概要

Redux / Flux の Action にはオブジェクトであれば何を指定しても大丈夫ですが、非公式のコーディング規約([flux-standard-action](https://github.com/acdlite/flux-standard-action))があり、Redux の公式ドキュメントでもこのコーディング規約に従うことを奨励しています。
[```redux-actions```](https://github.com/acdlite/redux-actions) を使って、サンプルコードをコーディング規約に従った Action に修正します。

# 準備

[Middleware編](https://github.com/yasuhiro-okada-aktsk/sample_redux/tree/middleware) の続き(tag/middleware)を使います。

# Action の構造

flux-standard-action の規約では Action は次のような構造のオブジェクトになります。

```js:Action
{
  type: FOO_TYPE,      // must
  payload: {object},   // optional
  meta: {object},      // optional
  error: false, true, undefined, null, ... // optional
}
```

```type``` には文字列またはシンボルで Action のタイプを設定します。
```payload``` にはデータを設定します。```error``` が ```true``` の場合は ```Error``` オブジェクトを設定します。
```meta``` には追加のデータを設定できます。

```error``` はエラーの状態を表します。（```true``` / ```false``` 以外も指定できます）
redux の [```examples/real-world```](https://github.com/reactjs/redux/tree/master/examples/real-world) のように従来は ```XXX_SUCCESS``` ```XXX_FAILURE``` のような type を定義していましたが、今後は ```error``` を使って判定します。

# 簡単な例

```RESET_ERROR_MESSAGE``` が ```type``` のみの Action なので、まずはこちらに ```redux-actions``` を使います。

```redux-actions``` をインストールします。

```bash
npm i redux-actions --save
```

```actions/index.js``` を修正します。

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

export let resetErrorMessage = createAction(RESET_ERROR_MESSAGE);

/* オリジナル
export function resetErrorMessage() {
  return {
    type: RESET_ERROR_MESSAGE
  };
*/
```

```createAction``` の第１引数に ```type``` を指定すると *ActionCreator* が作成されます。
※ 名前は ```createAction``` ですが、作られるのは ActionCreator です。

次にエラーメッセージの表示を修正します。

# ```redux-actions``` の問題点

今回のサンプルには、エラーメッセージを表示する機能があるため、```action.error``` を使いたかったのですが、機能が実装されておらず [プルリク](https://github.com/acdlite/redux-actions/pull/16)のマージ待ちです。

今回は ```meta``` を使ってエラーを表現することにします。

```payload``` には空を、```meta``` には```{error: {message: <message>}}``` を設定する仕様にします。

```js:actions/index.js
import { createErrorMeta } from './error'

function empty() {
  return {}
}

export let showErrorMessage = createAction(SHOW_ERROR_MESSAGE, empty, createErrorMeta);

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

```js:actions/error.js
export function createErrorMeta(message) {
  return {
    error: {
      message: message
    }
  }
}
```

```createAction``` の第２引数には payload creator を、第３引数には meta creator を指定します。

```createAction``` で生成した Action creator に指定した引数は payload creator / meta creator の両方に指定されます。
例えば、```showErrorMessage``` の場合、 ```showErrorMessage("test")``` のように呼ぶと、```empty``` と ```createErrorMeta``` の両方に ```"test"``` が渡されます。

# reducer

reducer は ```handleActions``` を使って実装できます。
モーダルの表示部分で実装します。

```js:action/index.js
export let showModal = createAction(SHOW_MODAL, (title, message) => ({title: title, message: message}));
export let hideModal = createAction(HIDE_MODAL);
```

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

const messageModal = handleActions({
  SHOW_MODAL: (state, action) => ({
    visibility: "show",
    title: action.payload.title,
    message: action.payload.message
  }),

  HIDE_MODAL: (state, action) => ({
    visibility: "hide"
  })
}, messageModalInitial);

/*
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;
  }
}
*/
```

# 最後に

すべてを修正したものは [tag/redux-actions](https://github.com/yasuhiro-okada-aktsk/sample_redux/tree/redux-actions) です。

標準的なコーディング規約や手法にしたがっておくと、新しい人が参加するときに理解しやすくなります。
```redux-actions``` のようなライブラリは規約従ったコードを書けるだけでなく、ボイラープレートコードも減らしてくれるので、有効に活用していきたいです。

# 参考

* [acdlite/flux-standard-action](https://github.com/acdlite/flux-standard-action)
* [acdlite/redux-actions](https://github.com/acdlite/redux-actions)