redux の repatch middleware を実装しようとしたメモ

色々コンテキスト略。自分用の作業メモ。

jaystack/repatch を redux の middleware に移植しようとしたが、いくつか問題があった。

  • ActionCreator が reducer を返す、という基本コンセプトを維持するとする
  • しかし repatch はストアがシングルトン前提なので、そのまま移植すると combineReducers で破綻する
  • ので reducer関数の参照を action.type として使って、reducer を当てる対象としてスコープをコントロールできるのでは
  • というのを考えたが、dispatch 時にreducer参照が必要になってしまい、どう考えてもAPIがきれいにならない
  • よく考えたら、 connect の第二引数で bindActionCreators する時にdispatchと関数一覧を渡してどうにか出来るのでは?
  • ということで * as actions するとして、 規約 で default を reducer、残りを ActionCreator と決め打ってしまうと綺麗になるかもしれない

練った結果、こういうのが作れるのがわかった

// reducers/index.js
import { combineReducers } from 'redux'
import counter from './counter'
export default combineReducers({counter})

// reducers/counter.js
import { createReducer } from 'apatcher-redux'

export type Counter = {
  value: number
}

export const addAsync = (n: number) => async (state: Counter) => ({
  value: state.value + n
})

export default createReducer({ value: 0 })

// containers/Counter.js
import * as counter from '../reducers/counter'
import * as foo from '../reducers/foo'
import { bindReducersToActions } from 'apatcher-redux'

connect(
  (state: Counter) => state,
  bindReducersToActions({counter, foo})
)(function A(props) {
  return (
    <div>
      <button onClick={() => props.counter.addAsync(1)}>Add 1</button>
      <button onClick={() => props.counter.addAsync(2)}>Add 2</button>
    </div>
  )
})

問題はあって

  • 暗に default が reducer としてたり、ファイルスコープに規約を持ち込んでしまってるのがあまりよくなさそう
  • connect 側から見てactionsに異常に型が付けづらい
  • テスト時に専用のコンテキストを組み立てる手間が多くて面倒
  • reduxに対して部分的に使えるようにしたところで、パラダイムが違いすぎて混ぜるのは見通しがすごく悪い

これ実装したところで生の redux 使うのとどっちが楽かというところで、結局強いメリットを示せないのではないか、という結論になって一晩寝かせて考えたほうが良さそう。