react create app javascript flux redux code-splitting

javascript - create react app title



¿Cómo cargar dinámicamente reductores para la división de código en una aplicación Redux? (6)

Actualización: vea también cómo lo hace Twitter .

Esta no es una respuesta completa, pero debería ayudarlo a comenzar. Tenga en cuenta que no estoy descartando los reductores antiguos, solo estoy agregando otros nuevos a la lista de combinaciones. No veo ninguna razón para tirar los viejos reductores, 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.

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); }) } }; // ... }

Puede haber una forma más ordenada de expresar esto: solo estoy mostrando la idea.

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 reductor combinado no es bueno. replaceReducer() las fuentes de Redux y encontré la función replaceReducer() . Parece ser lo que quiero.

Podría crear un reductor combinado para cada parte de mi aplicación y usar replaceReducer() cuando me muevo entre las partes de la aplicación.

¿Es este un buen enfoque?



Ahora hay un módulo que agrega reductores de inyección en la tienda redux. Se llama Redux Injector .

Aquí está cómo usarlo:

  1. No combine reductores. En su lugar, póngalos en un objeto (anidado) de funciones como lo haría normalmente pero sin combinarlos.

  2. Utilice createInjectStore desde redux-injector en lugar de createStore desde redux.

  3. Inyecte nuevos reductores con injectReducer.

Aquí hay un ejemplo:

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);

Divulgación completa: soy el creador del módulo.


Aquí hay otro example con la división de código y las 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 store 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.


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-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 } }

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} Object var coreReducers = require(''./reducers/core'') var reducerRegistry = new ReducerRegistry(coreReducers)

Luego, al configurar la tienda y las rutas, utilice 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:

También cubre la configuración necesaria para permitir la recarga en caliente para todos sus reductores.


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 toda 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 esté cargado 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