Redux 基礎：Middleware 編

# 概要

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

# 準備

[Reducer 編](http://qiita.com/yasuhiro-okada-aktsk/items/9d9025cb58ffba35f864)の続き([```tag/reducer```](https://github.com/yasuhiro-okada-aktsk/sample_redux/tree/reducer))から行います。
※ 実装完了は[```tag/middleware```](https://github.com/yasuhiro-okada-aktsk/sample_redux/tree/middleware)

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

# Middleware とは

Reducer の実行前後に処理を追加するための仕組みです。

Middleware は次のような形式です。

```js:middleware
store => next => action => {
   :
}
```

```next``` は次の middleware を表します。　```store``` と ```action``` は名前の通りです。

何もしない middleware は次のようになります。

```js:middleware
store => next => action => {
  next(action);
}
```
middleware はすべての Action に適用されるため、これを基本形（つまり何もしない）にして、前後に追加の処理をしたり、特定の条件の場合のみ別の処理をすることになります。

```next``` は middleware のチェインなので、次々と middleware が呼ばれ、最終的には dispatch (さらにその先の Reducer) が呼ばれます。

つまり、１つの middleware で見ると、次のようになります。

```js:middleware
store => next => action => {
  // 前の state
  next(action);
  // 後の state
}
```

例えば、reducer の実行前後のログを取りたい場合、次のようになります。

```js:logger
export const logger = store => next => action => {
  console.log("before: %O", store.getState());
  next(action);
  console.log("after: %O", store.getState());
};
```

middleware は次のようなことができます。

* ```next()``` を複数回呼ぶ
* ```next()``` を非同期で呼ぶ
* ```next()``` に新しい Action を渡す

この性質を利用して、API 通信を middleware で行い、「通信前に通信中状態の Action」、通信結果に応じて、「通信結果の Action」「通信失敗状態の Action」「リトライ」などの処理を middleware に閉じ込めることができます。

# Middleware の作成

上記の ```logger``` をサンプルに追加してみます。
```middleware/index.js``` を作成します。

```js:middleware/index.js
export const logger = store => next => action => {
  console.log("before: %O", store.getState());
  next(action);
  console.log("after: %O", store.getState());
};
```

```store/configureStore.dev.js``` に middleware を追加します。
※ 高機能なロガーの ```createLogger()``` はコメントアウトしてください。

```js:store/configureStore.dev.js
import { logger } from "../middleware"

const finalCreateStore = compose(
  applyMiddleware(logger),  // 追加
  applyMiddleware(thunk),
  reduxReactRouter({ routes, createHistory }),
  //applyMiddleware(createLogger()),  // コメントアウト
  DevTools.instrument()
)(createStore);
```

実行して、Action を実行すると console にログが表示されます。

# 非同期処理

本当の通信は早すぎるので、タイマーを使ったダミーのサンプルを作成します。

まず、「通信中」「通信結果」の state を作る Reducer と、その Action を作ります。

```js:reducers/index.js
const aServiceInitial = {
  isFetching: false,
  data: ""
};

function aService(state = aServiceInitial, action) {
  const { type, data } = action;

  switch (type) {
    case ActionTypes.SERVICE_GET_DATA:
      return Object.assign({},
        state,
        {
          isFetching: true
        });

    case ActionTypes.SERVICE_SUCCESS:
      return {
        isFetching: false,
        data: data
      };

    default:
      return state;
  }
}
```

```js:actions/index.js
export const SERVICE_GET_DATA = "SERVICE_GET_DATA";
export const SERVICE_SUCCESS = "SERVICE_SUCCESS";

export function serviceGetData(title, message) {
  return {
    type: SERVICE_GET_DATA,
    api: "http://example.com",
    success: serviceSuccess
  }
}

export function serviceSuccess(data) {
  return {
    type: SERVICE_SUCCESS,
    data: data
  }
}
```

さらに２つの state を表示する UI と Action を発行するボタンを追加します。

```js:containers/App.js
  handleFetch(e) {
    this.props.serviceGetData();
    e.preventDefault();
  }

  renderFetch() {
    const { aService } = this.props;

    return (
      <div>
        <button type="button" className="btn btn-primary btn-lg" onClick={::this.handleFetch}>
          fetch
        </button>

        <div>data : {aService.data}</div>
        <div>{aService.isFetching ? "fetching..." : ("")}</div>
      </div>
    );
  }
```

middleware の仕様として Action に ```action.api``` があればその url に通信し、```action.success``` に受信したデータを渡すことにします。

```js:middleware/index.js
export const api = store => next => action => {
  next(action);

  if (action.api) {
    setTimeout(() => {
      next(action.success(store.getState().aService.data + store.getState().aService.data.length))
    }, 3000);
  }
};
```

１つ目の ```next(action)``` は何もしない処理で ```api``` プロパティを持っていない場合は、ここだけが実行され何も影響を与えません。
```SERVICE_GET_DATA``` の場合も一旦何もせず Reducer を実行させることで ```isFetching``` を ```true``` にし、UI に "fetching..." と表示させています。

次に ```action.api``` があるときは、３秒後（通信のシミュレーションです）に ```next(action ~~)``` を呼んでいます。
```SERVICE_GET_DATA``` の場合、```serviceGetData()``` が ```action.success``` にセットされているため、```SERVICE_SUCCESS``` Action が実行されます。

# 最後に

middleware は Reducer (実際はdispatch) を非同期に実行したり、Action を変えたりできます。
うまく設計することで、Action や Reducer を単純にできます。

# 参考

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