如何在Redux应用程序中dynamic加载用于代码拆分的reducer?

我正在迁移到Redux。

我的应用程序由很多部分(页面,组件)组成,所以我想创build许多reducer。 Redux示例显示我应该使用combineReducers()来生成一个reducer。

另外据我所知Redux应用程序应该有一个商店,它是一旦应用程序启动时创build。 当商店正在创build时,我应该通过我的组合减速机。 如果应用程序不是太大,这是有道理的。

但是,如果我构build多个JavaScript包,该怎么办? 例如,每个应用程序页面都有自己的包。 我认为在这种情况下,联合减速机是不好的。 我查看了Redux的源代码,发现了replaceReducer()函数。 这似乎是我想要的。

我可以为我的应用程序的每个部分创build组合的replaceReducer()器,并在应用程序的各个部分之间移动时使用replaceReducer()

这是一个好方法吗?

这不是一个完整的答案,但应该帮助你开始。 请注意,我并没有扔掉旧的减速器 –我只是在组合列表中添加新的减速器 。 我看不出有什么理由把旧的reducer扔掉 – 即使在最大的应用程序中,你也不可能有成千上万个dynamic模块,这是你可能想要断开应用程序中一些reducer的地方。

reducers.js

 import { combineReducers } from 'redux'; import users from './reducers/users'; import posts from './reducers/posts'; export default function createReducer(asyncReducers) { return combineReducers({ users, posts, ...asyncReducers }); } 

store.js

 import { createStore } from 'redux'; import createReducer from './reducers'; export default function configureStore(initialState) { const store = createStore(createReducer(), initialState); store.asyncReducers = {}; return store; } export function injectAsyncReducer(store, name, asyncReducer) { store.asyncReducers[name] = asyncReducer; store.replaceReducer(createReducer(store.asyncReducers)); } 

routes.js

 import { injectAsyncReducer } from './store'; // Assuming React Router here but the principle is the same // regardless of the library: make sure store is available // when you want to require.ensure() your reducer so you can call // injectAsyncReducer(store, name, reducer). function createRoutes(store) { // ... const CommentsRoute = { // ... getComponents(location, callback) { require.ensure([ './pages/Comments', './reducers/comments' ], function (require) { const Comments = require('./pages/Comments').default; const commentsReducer = require('./reducers/comments').default; injectAsyncReducer(store, 'comments', commentsReducer); callback(null, Comments); }) } }; // ... } 

可能有更好的expression方式 – 我只是展示了这个想法。

这是我在当前的应用程序中实现它(基于由GitHub问题的Dan的代码!)

 // Based on https://github.com/rackt/redux/issues/37#issue-85098222 class ReducerRegistry { constructor(initialReducers = {}) { this._reducers = {...initialReducers} this._emitChange = null } register(newReducers) { this._reducers = {...this._reducers, ...newReducers} if (this._emitChange != null) { this._emitChange(this.getReducers()) } } getReducers() { return {...this._reducers} } setChangeListener(listener) { if (this._emitChange != null) { throw new Error('Can only set the listener for a ReducerRegistry once.') } this._emitChange = listener } } 

在引导应用程序时创build一个registry实例,传递将包含在入口包中的reducer:

 // coreReducers is a {name: function} Object var coreReducers = require('./reducers/core') var reducerRegistry = new ReducerRegistry(coreReducers) 

然后在configuration存储和路由时,使用一个函数,您可以给reducerregistry:

 var routes = createRoutes(reducerRegistry) var store = createStore(reducerRegistry) 

这些function如下所示:

 function createRoutes(reducerRegistry) { return <Route path="/" component={App}> <Route path="core" component={Core}/> <Route path="async" getComponent={(location, cb) => { require.ensure([], require => { reducerRegistry.register({async: require('./reducers/async')}) cb(null, require('./screens/Async')) }) }}/> </Route> } function createStore(reducerRegistry) { var rootReducer = createReducer(reducerRegistry.getReducers()) var store = createStore(rootReducer) reducerRegistry.setChangeListener((reducers) => { store.replaceReducer(createReducer(reducers)) }) return store } 

这是一个基本的实例,它是使用这个设置创build的,它的来源是:

  • 资源

它还涵盖必要的configuration,以便为所有减速器启用热重新加载。

现在有一个模块可以将reduce注入到redux存储中。 它被称为Redux注射器。 https://github.com/randallknutson/redux-injector

以下是如何使用它:

  1. 不要组合减速器。 而是把它们放在一个(嵌套的)函数对象中,就像你通常一样,但没有把它们合并起来。

  2. 使用redux-injector中的createInjectStore代替redux中的createStore。

  3. 用injectReducer注入新的reducer。

这里是一个例子:

 import { createInjectStore, injectReducer } from 'redux-injector'; const reducersObject = { router: routerReducerFunction, data: { user: userReducerFunction, auth: { loggedIn: loggedInReducerFunction, loggedOut: loggedOutReducerFunction }, info: infoReducerFunction } }; const initialState = {}; let store = createInjectStore( reducersObject, initialState ); // Now you can inject reducers anywhere in the tree. injectReducer('data.form', formReducerFunction); 

完全披露:我是模块的创build者。

截至2017年10月:

  • Reedux

    实现了Dan所build议的,而没有触及你的商店,你的项目或你的习惯

还有其他的库,但是它们可能有太多的依赖关系,less的例子,复杂的用法,与一些中间件不兼容或者需要你重写你的状态pipe理。 从Reedux的介绍页复制:

  • 终极版模块
  • 终极版模块build设者
  • 终极版,堆
  • paradux
  • 终极版dynamic减速器
  • 终极版注射器
  • 终极版,DYNAMIX

这里是另一个代码分割和Redux商店的例子 ,在我看来非常简单和优雅。 我认为这对那些正在寻找工作解决scheme的人来说可能相当有用。

这个存储有点简单,它不会强制你在你的状态对象中有一个命名空间(reducer.name),当然可能会与名称有冲突,但是你可以通过为你的reducer创build一个命名约定来控制它应该没事。

Interesting Posts