Django+Reactで学ぶプログラミング基礎(36): Reduxチュートリアル(MiddlewareでStoreをカスタマイズ)

##### [前回] [Django+Reactで学ぶプログラミング基礎(35): Reduxチュートリアル(TodoアプリのStoreをカスタマイズ)](https://qiita.com/mingchun_zhao/items/10695a21192168994396)

## はじめに

前回は、EnhancerとMiddlewareを用いてStoreをカスタマイズしました。
今回は、Middlewareの続きです。

## 今回の内容

[Todoアプリの構築](https://redux.js.org/tutorials/fundamentals/part-4-store#writing-custom-middleware)

- カスタムMiddlewareの作成
- Middlewareのユースケース

## カスタムMiddlewareの作成

- 独自のMiddlewareを作成し、特定動作をReduxアプリに追加
- 下記Middlewareは、3つのネストされた関数として記述されている
    - 一番外側の関数`exampleMiddleware`は、`Middleware`そのもの
        - `applyMiddleware`により呼び出され、storeAPIオブジェクトを引数として受け取る
            - storeの`{dispatch、getState}`関数を含む
        - dispatch関数を呼び出すと、actionが送信され、Middlewareパイプラインが開始する
            - これは一度だけ呼び出される
    - 真ん中の関数`wrapDispatch`は、パイプラインの`next`Middleware
        - 引数として`next`を受け取る
        - `next(action)`を呼び出すと、パイプライン内の`next`Middlewareにactionが渡される
            - これも一度だけ呼び出される
        - このMiddlewareがシーケンスの最後のMiddlewareの場合
            - `next`はオリジナルの`store.dispatch`関数となる
    - 一番内側の関数`handleAction`は、actionがdispatchされるたびに呼び出される
        - 引数として現在のactionを受け取る
```
// Middleware written as ES5 functions

// Outer function:
function exampleMiddleware(storeAPI) {
  return function wrapDispatch(next) {
    return function handleAction(action) {
      // Do anything here: pass the action onwards with next(action),
      // or restart the pipeline with storeAPI.dispatch(action)
      // Can also use storeAPI.getState() here

      return next(action)
    }
  }
}
```

- 上記Middlewareを、ES6のアロー関数を使用し、短く書ける
    - アロー関数にreturnステートメントが必要ない
```
const anotherExampleMiddleware = storeAPI => next => action => {
  // Do something in here, when each action is dispatched

  return next(action)
}
```

### カスタムMiddlewareの例1: 更新後のstateをコンソール出力する

- actionがdispatchされると、Middlewareパイプライの下記処理が走る
    - `handleAction`関数により、`dispatching`メッセージがコンソール出力される
    - actionを`next`セクションに渡す
        - `next`セクションは、別のMiddleware、またはオリジナルの`store.dispatch`となる
        - 最終的に、reducerが実行され、stateが更新される
        - `next`関数が返される
    - `storeAPI.getState()`を呼び出し、新しいstateを確認
    - 最後に、`next`Middlewareから得られた結果値を返す
```
const loggerMiddleware = storeAPI => next => action => {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', storeAPI.getState())
  return result
}
```

- すべてのMiddlewareは任意の値を返すことができる
- `store.dispatch()`を呼び出すと、最終的返却されるのはパイプラインの最初Middlewareの戻り値
    - 以下の例で、返却されるのは`Hello!`
        - オリジナル`store.dispatch`の結果ではない
```
const alwaysReturnHelloMiddleware = storeAPI => next => action {
  const originalResult = next(action);
  // Ignore the original result, return something else
  return 'Hello!'
}

const middlewareEnhancer = applyMiddleware(alwaysReturnHelloMiddleware)
const store = createStore(rootReducer, middlewareEnhancer)

const dispatchResult = store.dispatch({type: 'some/action'})
console.log(dispatchResult)
// log: 'Hello!'
```

### カスタムMiddlewareの例2: 内部で非同期ロジックを実行

- Middlewareはdispatchされた特定actionのみ処理可能
- Middlewareは、内部で非同期ロジックを実行可能
    - 以下の例で、`todos/todoAdded`actionが発生したら
        - 1秒遅延後に`action.payload`をコンソール出力
```
const delayedMessageMiddleware = storeAPI => next => action => {
  if (action.type === 'todos/todoAdded') {
    setTimeout(() => {
      console.log('Added a new todo: ', action.payload)
    }, 1000)
  }

  return next(action)
}
```

## Middlewareのユースケース

- dispatchされたactionに対し、下記追加処理を実行
    - コンソールにログ出力
    - タイムアウトを設定
    - 非同期API呼び出し
    - acitonを変更
    - actionを一時停止するか、完全停止

- Middlewareに、副作用のあるロジックを含められる
    - dispatchを変更し、プレーンでないオブジェクトをactionとして受け入れられる
        - プレーンでないオブジェクトとは
            - Class
            - Promise
            - function
            - その他組み込みJSタイプ
                - Map
                - Set Promise
                - Date

## おわりに

Redux Middlewareを使用し、Storeをカスタマイズしました。
次回も続きます。お楽しみに。

##### [次回] [Django+Reactで学ぶプログラミング基礎(37): Reduxチュートリアル(UIレイヤーのReactと統合)](https://qiita.com/mingchun_zhao/items/65bde650c9aaad5d252a)