Как динамически загружать редукторы для разделения кода в приложении Redux?
Я собираюсь перейти на Redux.
мое приложение состоит из множества частей (страниц, компонентов), поэтому я хочу создать много редукторов. Примеры Redux показывают, что я должен использовать combineReducers()
для создания одного редуктора.
также, как я понимаю, приложение Redux должно иметь один магазин, и он создается после запуска приложения. Когда магазин создается, я должен передать свой комбинированный редуктор. Это имеет смысл если приложение не слишком большой.
но что, если Я создаю более одного пакета JavaScript? Например, каждая страница приложения имеет свой собственный пакет. Я думаю, что в этом случае один комбинированный редуктор не хорошо. Я просмотрел источники Redux и нашел
5 ответов:
обновление: см. также как Твиттер делает это.
это не полный ответ, но должны помочь вам начать работу. Обратите внимание, что я не выбрасывать старые редукторы - Я просто добавляю новые в список комбинаций. Я не вижу причин выбрасывать старые редукторы-даже в самом большом приложении вы вряд ли будете иметь тысячи динамических модулей, что является точкой, где вы может хочу отключить некоторые редукторы в приложение.
редукторы.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. Это называется Возвращение инжектора. https://github.com/randallknutson/redux-injector
вот как его использовать:
не комбинируйте редукторы. Вместо этого поместите их в (вложенный) объект функций, как обычно, но без их объединения.
используйте createInjectStore из redux-injector вместо 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 магазинов, довольно простой и элегантный, на мой взгляд. Я думаю, что это может быть очень полезно для тех, кто ищет рабочее решение.
этой магазине немного упрощено это не заставляет вас иметь пространство имен (reducer.name) в вашем объекте состояния, конечно, может быть столкновение с именами, но вы можете контролировать это, создавая соглашение об именах для своих редукторов, и оно должно быть штраф.