Inject reducer arbitrarily rather than top level for redux store to replace reducer

When writing a big react redux application or any SPA using redux, there is a need to split code with webpack and load reducers asynchronously.

The way to load reducer asynchronously is utilizing redux store replaceReducer. There is typical sample of inject reducer like injectReducerfunction in https://github.com/davezuko/react-redux-starter-kit/blob/master/src/store/reducers.js. It only can replace the top level reducer in asyncReducers . For example.

asyncReducers = {
products: combineReducers({
list: productListReducer,
one: productReducer
}),
orders: ordersReducer
}

The injectReducer function only can replace products or orders which is top level in asyncReducers.

But there might be many chance that only asynchronously inject reducer with top one not enough since the split javascript file is still big.

Here introduce a way to inject reducer in arbitrary nested level.

The injectReducer function api change a little bit, the key is in the form of products or products.list and reducer is pure reducer not one by combineReducers . Then we can use key which is . separated to build nested object of asyncReducers which is kind of tree structure and leaf value is the pure reducer. With this information, it is easy to use combineReducers to rebuild the root reducer which is used by redux store replaceReducerfunction.

const replaceAsyncReducers = (rootReducers, keys, reducer) => {
let key = keys.shift()
if (keys.length === 0) {
rootReducers[key] = reducer
return
}
if (rootReducers[key] === undefined) rootReducers[key] = {}
let nextRootReducers = rootReducers[key]
return replaceAsyncReducers(nextRootReducers, keys, reducer)
}
const combineAsyncReducers = (asyncReducers) => {
if (typeof asyncReducers !== 'object') return asyncReducers
let combineReducerObject = {}
for (let prop in asyncReducers) {
if (!asyncReducers.hasOwnProperty(prop)) continue
let value = asyncReducers[prop]
if (typeof value === 'object') {
combineReducerObject[prop] = combineAsyncReducers(value)
} else if (typeof value === 'function') {
combineReducerObject[prop] = value
}
}
return combineReducers(combineReducerObject)
}
export const makeRootReducer = (asyncReducers) => {
let newAsyncReducers = {}
console.log(asyncReducers)
for (let key in asyncReducers) {
if (!asyncReducers.hasOwnProperty(key)) continue
newAsyncReducers[key] = combineAsyncReducers(asyncReducers[key])
}
  return combineReducers({
location: locationReducer,
...asyncReducers
})
}
export const injectReducer = (store, { key, reducer }) => {
let keys = key.split('.')
replaceAsyncReducers(store.asyncReducers, keys, reducer)
// store.asyncReducers[key] = reducer
store.replaceReducer(makeRootReducer(store.apolloClient, store.asyncReducers))
}

With this change, we can inject reducer anywhere with injectReducer(store, { key: 'products.list', reducer: productListReducer }) .

上一篇:ASP.NET-FineUI开发实践-3


下一篇:玩转阿里云EMR三部曲-高级篇 交互式查询及统一数据源