¿Cómo cargar dinámicamente reductores para la división de código en una aplicación Redux?
189
Voy a migrar a Redux.
Mi aplicación consta de muchas partes (páginas, componentes), por lo que quiero crear muchos reductores. Los ejemplos de Redux muestran que debería usar combineReducers()para generar un reductor.
Además, según tengo entendido, la aplicación Redux debe tener una tienda y se crea una vez que se inicia la aplicación. Cuando se crea la tienda, debería pasar mi reductor combinado. Esto tiene sentido si la aplicación no es demasiado grande.
¿Pero qué pasa si construyo más de un paquete de JavaScript? Por ejemplo, cada página de la aplicación tiene su propio paquete. Creo que en este caso el único reductor combinado no es bueno. Revisé las fuentes de Redux y encontré la replaceReducer()función. Parece ser lo que quiero.
Podría crear un reductor combinado para cada parte de mi aplicación y usarlo replaceReducer()cuando me muevo entre partes de la aplicación.
Esta no es una respuesta completa, pero debería ayudarlo a comenzar. Tenga en cuenta que no estoy desechando los reductores antiguos, solo estoy agregando otros nuevos a la lista de combinaciones. No veo ninguna razón para deshacerse de los reductores antiguos, incluso en la aplicación más grande es poco probable que tenga miles de módulos dinámicos, que es el punto en el que es posible que desee desconectar algunos reductores en su aplicación.
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){// ...constCommentsRoute={// ...
getComponents(location, callback){
require.ensure(['./pages/Comments','./reducers/comments'],function(require){constComments= require('./pages/Comments').default;const commentsReducer = require('./reducers/comments').default;
injectAsyncReducer(store,'comments', commentsReducer);
callback(null,Comments);})}};// ...}
Puede haber una forma más ordenada de expresar esto: solo estoy mostrando la idea.
Me encantaría ver este tipo de funcionalidad agregada al proyecto. La capacidad de agregar reductores dinámicamente es imprescindible cuando se trata de división de código y aplicaciones grandes. Tengo subárboles enteros a los que algunos usuarios no pueden acceder y cargar todos los reductores es un desperdicio. Incluso con redux-ignore, las aplicaciones grandes realmente pueden acumular reductores.
JeffBaumgardt
2
A veces, es un desperdicio mayor 'optimizar' algo intrascendente.
XML
1
Espero que el comentario anterior tenga sentido ... ya que me quedé sin espacio. Pero básicamente no veo una manera fácil de combinar los reductores en una sola rama en nuestro árbol de estado cuando se cargan dinámicamente desde diferentes rutas /homepagey luego se carga más de esa rama cuando el usuario va a su profile.Un ejemplo de cómo hacer esto, sería genial. De lo contrario, me resulta difícil aplanar mi árbol de estado o tengo que tener nombres de sucursales muy específicos user-permissionsyuser-personal
Así es como lo implementé en una aplicación actual (¡basada en el código de Dan de un problema de GitHub!)
// Based on https://github.com/rackt/redux/issues/37#issue-85098222classReducerRegistry{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){thrownewError('Can only set the listener for a ReducerRegistry once.')}this._emitChange = listener
}}
Cree una instancia de registro al iniciar su aplicación, pasando reductores que se incluirán en el paquete de entrada:
// coreReducers is a {name: function} Objectvar coreReducers = require('./reducers/core')var reducerRegistry =newReducerRegistry(coreReducers)
Luego, al configurar la tienda y las rutas, use una función que puede asignar al registro reductor para:
var routes = createRoutes(reducerRegistry)var store = createStore(reducerRegistry)
Donde estas funciones se parecen a:
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
}
Aquí hay un ejemplo en vivo básico que se creó con esta configuración y su fuente:
implementa lo que Dan sugirió y nada más, sin tocar tu tienda, tu proyecto o tus hábitos
También hay otras bibliotecas, pero pueden tener demasiadas dependencias, menos ejemplos, uso complicado, son incompatibles con algunos middlewares o requieren que reescriba su administración de estado. Copiado de la página de introducción de Reedux:
Los módulos proporcionan los siguientes beneficios:
Los módulos se pueden reutilizar fácilmente en la aplicación o entre múltiples aplicaciones similares.
Los componentes declaran los módulos que necesitan y redux-dynamic-modules asegura que el módulo se cargue para el componente.
Los módulos se pueden agregar / quitar de la tienda dinámicamente, ej. cuando se monta un componente o cuando un usuario realiza una acción
Caracteristicas
Agrupe los reductores, el middleware y el estado en un solo módulo reutilizable.
Agregue y elimine módulos de una tienda Redux en cualquier momento.
Use el componente incluido para agregar automáticamente un módulo cuando se representa un componente
Las extensiones proporcionan integración con bibliotecas populares, incluidas redux-saga y redux-observable
Escenarios de ejemplo
No desea cargar el código de todos sus reductores por adelantado. Defina un módulo para algunos reductores y use DynamicModuleLoader y una biblioteca como react-loadable para descargar y agregar su módulo en tiempo de ejecución.
Tiene algunos reductores / middleware comunes que deben reutilizarse en diferentes áreas de su aplicación. Defina un módulo e inclúyalo fácilmente en esas áreas.
Tiene un repositorio mono que contiene múltiples aplicaciones que comparten un estado similar. Cree un paquete que contenga algunos módulos y reutilícelos en sus aplicaciones
Aquí hay otro ejemplo con división de código y tiendas redux, bastante simple y elegante en mi opinión. Creo que puede ser bastante útil para aquellos que buscan una solución de trabajo.
Esta tienda está un poco simplificada, no lo obliga a tener un espacio de nombres (reducer.name) en su objeto de estado, por supuesto, puede haber una colisión con los nombres, pero puede controlar esto creando una convención de nomenclatura para sus reductores y debería estar bien.
/homepage
y luego se carga más de esa rama cuando el usuario va a suprofile.
Un ejemplo de cómo hacer esto, sería genial. De lo contrario, me resulta difícil aplanar mi árbol de estado o tengo que tener nombres de sucursales muy específicosuser-permissions
yuser-personal
Así es como lo implementé en una aplicación actual (¡basada en el código de Dan de un problema de GitHub!)
Cree una instancia de registro al iniciar su aplicación, pasando reductores que se incluirán en el paquete de entrada:
Luego, al configurar la tienda y las rutas, use una función que puede asignar al registro reductor para:
Donde estas funciones se parecen a:
Aquí hay un ejemplo en vivo básico que se creó con esta configuración y su fuente:
También cubre la configuración necesaria para permitir la recarga en caliente de todos sus reductores.
fuente
Ahora hay un módulo que agrega reductores de inyección en la tienda redux. Se llama Redux Injector .
Aquí está cómo usarlo:
No combine reductores. En su lugar, póngalos en un objeto (anidado) de funciones como lo haría normalmente pero sin combinarlos.
Use createInjectStore desde redux-injector en lugar de createStore desde redux.
Inyecte nuevos reductores con injectReducer.
Aquí hay un ejemplo:
Divulgación completa: soy el creador del módulo.
fuente
A partir de octubre de 2017:
Reedux
implementa lo que Dan sugirió y nada más, sin tocar tu tienda, tu proyecto o tus hábitos
También hay otras bibliotecas, pero pueden tener demasiadas dependencias, menos ejemplos, uso complicado, son incompatibles con algunos middlewares o requieren que reescriba su administración de estado. Copiado de la página de introducción de Reedux:
fuente
Lanzamos una nueva biblioteca que ayuda a modular una aplicación Redux y permite agregar / eliminar dinámicamente Reductores y middlewares.
Por favor, eche un vistazo a https://github.com/Microsoft/redux-dynamic-modules
Los módulos proporcionan los siguientes beneficios:
Los módulos se pueden reutilizar fácilmente en la aplicación o entre múltiples aplicaciones similares.
Los componentes declaran los módulos que necesitan y redux-dynamic-modules asegura que el módulo se cargue para el componente.
Caracteristicas
Escenarios de ejemplo
fuente
Aquí hay otro ejemplo con división de código y tiendas redux, bastante simple y elegante en mi opinión. Creo que puede ser bastante útil para aquellos que buscan una solución de trabajo.
Esta tienda está un poco simplificada, no lo obliga a tener un espacio de nombres (reducer.name) en su objeto de estado, por supuesto, puede haber una colisión con los nombres, pero puede controlar esto creando una convención de nomenclatura para sus reductores y debería estar bien.
fuente