Как динамически загружать редукторы для разделения кода в приложении Redux?
Я собираюсь мигрировать в Redux.
мое приложение состоит из множества частей (страниц, компонентов), поэтому я хочу создать много редукторов. Примеры Redux показывают, что я должен использовать combineReducers()
для создания одного редуктора.
также, как я понимаю, приложение Redux должно иметь один магазин, и он создается после запуска приложения. Когда магазин создается, я должен передать свой комбинированный редуктор. Это имеет смысл если приложение не слишком большой.
но что, если Я создаю более одного пакета JavaScript? Например, каждая страница приложения имеет собственный набор. Я думаю, что в этом случае один комбинированный редуктор не хорошо. Я просмотрел источники Redux и я нашел
5 ответов
обновление: см. также как Twitter делает это.
это не полный ответ, но должны помочь вам начать работу. Обратите внимание, что я не выбрасывать старые редукторы - Я просто добавляю новые в список комбинаций. Я не вижу причин выбрасывать старые редукторы-даже в самом большом приложении у вас вряд ли будут тысячи динамических модулей, в которых вы может хочу отключить некоторые редукторы в приложение.
редукторы.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
});
}
магазине.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));
}
маршруты.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);
})
}
};
// ...
}
может быть более аккуратный способ выразить это-я просто показываю идею.
вот как я реализовал его в текущем приложении (на основе кода Дэна из проблемы GitHub!)
// 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
}
}
создайте экземпляр реестра при загрузке вашего приложения, передавая редукторы, которые будут включены в набор записей:
// coreReducers is a {name: function} Object
var coreReducers = require('./reducers/core')
var reducerRegistry = new ReducerRegistry(coreReducers)
затем при настройке магазина и маршрутов используйте функцию, которую вы можете дать реестру редуктора:
var routes = createRoutes(reducerRegistry)
var store = createStore(reducerRegistry)
где эти функции выглядят примерно так:
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
}
вот основные живой пример, который был создан с этой настройкой, и его источник:
оно также покрывает необходимую конфигурацию для того чтобы включить горячий перезаряжать для всех ваших редукторов.
теперь есть модуль, который добавляет инъекционные редукторы в хранилище redux. Оно вызван инжектором Redux. https://github.com/randallknutson/redux-injector
вот как его использовать:
не комбинировать редукторы. Вместо этого поместите их в (вложенный) объект функций, как обычно, но без их объединения.
используйте createInjectStore из redux-инжектора вместо createStore из возвращение.
впрысните новые редукторы с injectReducer.
вот пример:
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);
полное раскрытие: я создатель модуля.
по состоянию на октябрь 2017:
-
реализует то, что предложил Дэн, и ничего больше, не касаясь вашего магазина, вашего проекта или ваших привычек
есть и другие библиотеки, но они могут иметь слишком много зависимостей, меньше примеров, сложное использование, несовместимы с некоторыми промежуточными программами или требуют переписать управление состоянием. Скопировано со страницы ввода Reedux:
вот еще пример с разделением кода и магазинами redux, довольно просто и элегантно, на мой взгляд. Я думаю, что это может быть очень полезно для тех, кто ищет рабочее решение.
этой магазине немного упрощено, это не заставляет вас иметь пространство имен (редуктор.имя) в вашем объекте состояния, конечно, может быть столкновение с именами, но вы можете контролировать это, создавая соглашение об именах для ваших редукторов, и оно должно быть штраф.