---
title: Redux の reducer を非同期取れるようにしてみた
tags: redux React
author: mizchi
slide: false
---
Reduxへの理解を深めるために、コードをいじってみて、どれぐらい大変か確認してみた。
**実験的なものなので、プロダクションでは使わないように**

https://github.com/mizchi/redux


## 発想

主にここ
 https://gist.github.com/mizchi/d4a8455ef56a7adc123a388b3a5eaaaf

redux の reducer は非同期も取りたい。具体的には `f(state: State, action: Action): State` ではなく `f(state: State, action: Action): State | Promise<T>` としたい


## できたもの

reducer が async/await で(Promiseで)書ける。

```js
export default async (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'INCREMENT_ASYNC':
      await new Promise(done => {
        setTimeout(done, 500)
      })
      return await Promise.resolve(state + 1)
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}
```

実際の挙動。

![](https://i.gyazo.com/6e67a6a0b8f86ba8b3fde9bbf7a68e44.gif)

## 感想

実際に作ってみると、ひたすら async await キーワードが伝搬していく感じだった

https://github.com/mizchi/redux/commit/601a4634a5f6fe08049d4fee3e67e3bfc410ae9a#diff-01b6e016dc33dc6cf4466e7abd376200R150

互換のものを作ろうとすると、どこまで仕様で、どこから仕様じゃないか理解してないと、この先書ける気がしなかった。似て非なるものを作るのは簡単。middleware を非互換に、subscribe を書き直すといい。

```
⋊> ~/p/redux on master  yarn test
 PASS  test/typescript.spec.js
 PASS  test/createStore.spec.js
 PASS  test/combineReducers.spec.js
 PASS  test/applyMiddleware.spec.js
 PASS  test/bindActionCreators.spec.js
 PASS  test/compose.spec.js
 PASS  test/utils/warning.spec.js

Test Suites: 7 passed, 7 total
Tests:       16 skipped, 51 passed, 67 total
Snapshots:   0 total
Time:        5.181s
Ran all test suites.
```

非同期APIを取ることでそもそもテスト側を障る必要もあり、互換というわけではない。
全然関係ないが、 jest のアサーションが非同期に対して貧弱すぎてクソという感想をもった。

## できなかったこと

- オリジナルの Middleware と互換にしたかったが、実行順の関係で、非同期用に書き直さないといけないとたぶんダメ。src/compose.js を読み切ればなんかできるかも。
- subscribe されたものが発火する順番が、オリジナルと変わってしまっている。

そもそも middleware 使わなくていいように reducer で非同期取れるようにしてるんだから、Middlewareは redux-logger が動くぐらいでいいのかもしれない。subscribe はなんとかしたい。

この辺で時間切れ

