Estado inmutable con Redux e Immutable.js

Sergio Xalambrí
Jun 27, 2016 · 3 min read

Redux nos propone tratar nuestro estado como inmutable. Sin embargo los objetos y array en JavaScript no lo son, lo que puede causar que mutemos directamente el estado por error.

Immutable.js es una librería creada por Facebook para usar colecciones de datos inmutables como listas, mapas, sets, etc. Usándolo con Redux nos permite expresar nuestro estado como colecciones de Immutable.js para evitarnos estos posibles problemas al mutar datos.

Usándolo en un reducer

La primera forma de usar Immutable.js en Redux es usándolo directo en un reducer. Simplemente definiendo el estado inicial como una colección inmutable y luego modificándolo según la acción despachada.

import { Map as map } from 'immutable';function reducer(state = map(), { type, payload }) {
switch (type) {
case 'ADD': {
return state.set(payload.id.toString(), map(payload));
}
default: {
return state;
}
}
}
export default reducer;

De esta forma podemos empezar a hacer uso de Immutable.js. Un pequeño detalle al usar mapas inmutables es que el key usado debe ser siempre un string, puede ser un número, pero por experiencia esto pueda dar errores de que Immutable.js no encuentre el valor al hacer colection.get(1), por esa razón cuando agregamos el dato a nuestro mapa usamos .toString() sobre el ID para evitarnos este problema.

Combinando reducers

Aunque es posible tener un único reducer para toda la aplicación, a medida que esta crece lo común es empezar a dividirlo en múltiples reducers y usar redux.combineReducers para unirlos en uno solo que usamos al crear el Store.

import { combineReducers } from 'redux';
import data from './reducers/data.js';
export default combineReducers({
data,
});

De esta forma nuestro estado ahora es un objeto con una propiedad data la cual posee nuestra colección inmutable, pero ¿Qué pasa si queremos que todo nuestro estado sea un conjunto de colecciones inmutables anidadas?

Combinando reducers con Immutable.js

Si decidimos tratar todo el estado como una colección inmutable debemos entonces hacer uso de redux-immutable. Esta librería nos ofrece una función combineReducers personalizada la cual funciona con exactamente la misma API que la oficial de Redux, por lo que hacer el cambio de una a otra consiste en cambiar de donde importamos la función.

import { combineReducers } from 'redux-immutable';
import data from './reducers/data.js';
export default combineReducers({
data,
});

Como vemos simplemente pasamos de importar desde redux a hacerlo desde redux-immutable, con ese simple cambio estamos usando Immutable.js en todo nuestro store, ahora cuando conectemos nuestros componentes a este podemos usar una sintaxis 100% de Immutable.js.

function getItem(state, props) {
return state
.get('data')
.get(props.id.toString())
.toJS(),
}

Ese selector por ejemplo se encarga de traerse del mapa de datos el item con el ID recibido como prop y devolverlo convertido a un objeto de JS común que podemos recibir en un componente y usarlo sin problemas.

Usándolo con react-router-redux

Resulta que la librería react-router-redux usada para combinar Redux y React Router no se lleva bien con un estado inmutable como el que crearíamos al usar redux-immutable.

Para poder usarlos juntos necesitamos crear nuestro propio reducer encargado de guardar y actualizar los datos de la ruta actual.

import { fromJS } from 'immutable';
import { LOCATION_CHANGE } from 'react-router-redux';
const initialState = fromJS({
locationBeforeTransitions: null,
});
export default function routeReducer(state = initialState, action) {
if (action.type === LOCATION_CHANGE) {
return state.merge({
locationBeforeTransitions: action.payload,
});
}
return state;
}

Con eso ya tenemos nuestro propio reducer para rutas. Ahora necesitamos cambiar el selector que usa react-router-redux para que convierta los datos a un objeto normal de JS. Para eso pasamos un tercer argumento syncHistoryWithStore.

import { browserHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
const history = syncHistoryWithStore(browserHistory, store, {
selectLocationState(state) {
return state.get('routing').toJS();
}
});

Nuestro selector entonces se encarga de obtener los datos desde la propiedad routing (o la que decidamos usar) y convertirlos a objetos planos de JS para que los pueda usar react-router-redux sin problemas y de forma completamente transparente.

Conclusión

Usar Immutable.js nos permite trabajar con un estado verdaderamente inmutable evitando problemas comunes como pueden ser mutar directamente una propiedad sin crear una copia del estado lo cual puede causar errores de inconsistencia de datos y dolores de cabeza a muchos desarrolladores.

Además que Immutable.js es bastante fácil de usar por lo que incluso nos facilita nuestro trabajo como desarrolladores enormemente, por lo que vale mucho la pena empezar a usarlo.

Sergio Xalambrí

Written by

Senior Software Engineer & Technical Writer— https://sergiodxa.com

React & Redux

Serie de artículos y tutoriales sobre Redux.js y React.js. Aprende como usar estas tecnologías para desarrollar aplicaciones web modernas más poderosas y usando las mejores prácticas.

Sergio Xalambrí

Written by

Senior Software Engineer & Technical Writer— https://sergiodxa.com

React & Redux

Serie de artículos y tutoriales sobre Redux.js y React.js. Aprende como usar estas tecnologías para desarrollar aplicaciones web modernas más poderosas y usando las mejores prácticas.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

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