この資料のアレ。
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のスコープの狭め方は有用。