Redux の reducer を非同期取れるようにしてみた

  • 7
    いいね
  • 0
    コメント

Reduxへの理解を深めるために、コードをいじってみて、どれぐらい大変か確認してみた。
実験的なものなので、プロダクションでは使わないように

https://github.com/mizchi/redux

発想

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

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

できたもの

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

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
  }
}

実際の挙動。

感想

実際に作ってみると、ひたすら 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 はなんとかしたい。

この辺で時間切れ