Redux: Módulos e “code-splitting”

Modularizando sua estrutura Redux como o Twitter Lite

Em aplicações grandes fazer “code-splitting” é necessários, e o Redux, como fica?

Redux em módulos

// data/notifications/index.jsconst initialState = [];
let notificationId = 0;
const createActionName = name => `app/notifications/${name}`;// reducer
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case ADD_NOTIFICATION:
return [...state, { ...action.payload, id: notificationId += 1 }];
case REMOVE_NOTIFICATION:
return state.slice(1);
default:
return state;
}
}
// selectors
export const selectAllNotifications = state => state.notifications;
export const selectNextNotification = state => state.notifications[0];
// actions
export const ADD_NOTIFICATION = createActionName(ADD_NOTIFICATION);
export const REMOVE_NOTIFICATION = createActionName(REMOVE_NOTIFICATION);
// action creators
export const addNotification = payload => ({ payload, type: ADD_NOTIFICATION });
export const removeNotification = () => ({ type: REMOVE_NOTIFICATION });
// components/NotificationView/connect.jsimport { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { removeNotification, selectNextNotification } from '../../data/notifications';
const mapStateToProps = createStructuredSelector({
nextNotification: selectNextNotification
});
const mapDispatchToProps = { removeNotification };
export default connect(mapStateToProps, mapDispatchToProps);
// components/NotificationView/index.jsimport connect from './connect';
export class NotificationView extends React.Component { /*...*/ }
export default connect(NotificationView);
// data/createStore.jsimport { combineReducers, createStore } from 'redux';
Import notifications from './notifications';
const initialState = /* localStorage ou servidor */const reducer = combineReducers({ notifications });
const store = createStore(reducer, initialState);
export default store;

Registro de reducers no Redux

// data/reducerRegistry.jsexport class ReducerRegistry {
constructor() {
this._emitChange = null;
this._reducers = {};
}
getReducers() {
return { ...this._reducers };
}
register(name, reducer) {
this._reducers = { ...this._reducers, [name]: reducer };
if (this._emitChange) {
this._emitChange(this.getReducers());
}
}
setChangeListener(listener) {
this._emitChange = listener;
}
}
const reducerRegistry = new ReducerRegistry();
export default reducerRegistry;
// data/notifications/index.js// [A] importando nosso módulo de registro
import reducerRegistry from '../reducerRegistry';
const initialState = [];
let notificationId = 0;
// [B] nome único desse módulo
const reducerName = 'notifications';
// [C] utilizamos o escopo em `${reducerName}`
const createActionName = name => `app/${reducerName}/${name}`;
// reducer
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case ADD_NOTIFICATION:
return [...state, { ...action.payload, id: notificationId += 1 }];
case REMOVE_NOTIFICATION:
return state.slice(1);
default:
return state;
}
}
// [D] registramos nosso reducer
reducerRegistry.register(reducerName, reducer);
// [E] seletores dependem do nome do reducer em `[reducerName]`
// selectors
export const selectAllNotifications = state => state[reducerName];
export const selectNextNotification = state => state[reducerName][0];
// actions
export const ADD_NOTIFICATION = createActionName(ADD_NOTIFICATION);
export const REMOVE_NOTIFICATION = createActionName(REMOVE_NOTIFICATION);
// action creators
export const addNotification = payload => ({ payload, type: ADD_NOTIFICATION });
export const removeNotification = () => ({ type: REMOVE_NOTIFICATION });
// data/createStore.jsimport { combineReducers, createStore } from 'redux';
import reducerRegistry from './reducerRegistry';
const initialState = /* localStorage ou servidor */// [A] Preservando o estado para um `reducer` que não foi carregado ainda
const combine = (reducers) => {
const reducerNames = Object.keys(reducers);
Object.keys(initialState).forEach(item => {
if (reducerNames.indexOf(item) === -1) {
reducers[item] = (state = null) => state;
}
});
return combineReducers(reducers);
};
const reducer = combine(reducerRegistry.getReducers());
const store = createStore(reducer, initialState);
// [B] Substitui a `store` atual toda vez que um `reducer` for registrado
reducerRegistry.setChangeListener(reducers => {
store.replaceReducer(combine(reducers));
});
export default store;

⭐ Créditos

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store