Redux は 概念的に Rx のサブセットであるという話

この資料のアレ。

mizchi.hatenablog.com

Reducer は単なる (State, Action) => State の関数で、redux.combineReducers は複数の reducer を名前空間でマップした新しい reducer にするもの。

Rx分かる人、Redux分かる人向けに、 redux.combineReducers を実装して、Rx.Observable.scan で reducer として実際に動くコードを書いた。

const Rx = require('rx')

const combineReducers = reducerMap => {
  const initialState = Object.entries(
    reducerMap
  ).reduce((acc, [key, reducer]) => {
    return Object.assign({}, acc, {
      [key]: reducer(undefined, { type: '__init' })
    })
  }, {})
  return (state = initialState, action) => {
    console.log('run combined reducer')
    return Object.entries(reducerMap).reduce((acc, [key, reducer]) => {
      return Object.assign({}, acc, {
        [key]: reducer(state[key], action)
      })
    }, {})
  }
}

const ADD = 'add'
const add = n => ({ type: ADD, payload: n })

const initialState = {
  value: 0
}

const counter = (state = initialState, action) => {
  switch (action.type) {
    case ADD:
      return { value: action.payload + state.value }
    default:
      return state
  }
}

const rootReducer = combineReducers({ counter })

const actions = Rx.Observable.from([add(1), add(2), add(3)])
const scanned = actions.scan(rootReducer, undefined) // need undefined to pass with reducer on init

scanned.subscribe(x => {
  console.log('subscribe', x)
})

最後の subscribe の console の代わりに ReactDOM.render にすれば、純粋関数な React Component が render できて、それをHoCとしてラップしてるのが ReactRedux.Provider というわけ。

Rx は 直接 Event Stream (Observale, Action Stream) を扱うけど、Redux は1つのEventを手がかりに前後の状態だけを記述するので、扱うスコープを限定したRx とも言える。スナップショットを確定するために 2つ以上または時系列的に差がある Stream 中の Event を使わないといけない場合、 Redux Middleware が出てくるけど、これをシュッと綺麗に書けるのは Rx の方。ただほとんどのケースでReduxのスコープの狭め方は有用。