Estado inmutable con Redux e Immutable.js

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.

 1 import { Map as map } from "immutable";
 2 function reducer(state = map(), { type, payload }) {
 3   switch (type) {
 4     case "ADD": {
 5       return state.set(payload.id.toString(), map(payload));
 6     }
 7     default: {
 8       return state;
 9     }
10   }
11 }
12 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 collection.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.

1 import { combineReducers } from "redux";
2 import data from "./reducers/data.js";
3 export default combineReducers({
4   data
5 });

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.

1 import { combineReducers } from "redux-immutable";
2 import data from "./reducers/data.js";
3 export default combineReducers({
4   data
5 });

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.

1 function getItem(state, props) {
2   return state
3     .get('data')
4     .get(props.id.toString())
5     .toJS(),
6 }

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.

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.