Introducción
Cada X años un grupo de desarrolladores, cogen piezas y buenas prácticas de aquí y allá y montan un patrón que cambia las reglas del juego. En esta ocasión le toca a Redux revolucionar el mundo del desarrollo ¿Pero que tiene este popular implementación del patrón Flux de especial que no tenga MVC, MVV, MV*^...? En este artículo vamos a ver sus puntos fuertes.
Si no conoces el patrón Redux te recomiendo esta serie de video tutoríales gratuitos, y el siguiente conjunto de ejemplos guiados.
Puntos fuertes
Vamos con los factores diferenciales que ofrece este pátron.
¿Arrancando proyecto? ¡No lo uses de primeras!
Cuando trabajamos con un patrón como MVVM o MVC, nos encontramos con que tenemos que adoptarlo desde el arranque del proyecto, o si no después nos va costar horas de refactoring poder introducirlo.
Si usamos este patrón, podemos arrancar nuestro proyecto utilizando sólo React, y más adelante podemos incorporar Redux. La única regla que debemos seguir es la de definir contenedores raíz donde vaya nuestro estado (sea mock o real) y nuestros eventos. Siguiendo esta aproximación, podremos introducir Redux cuando esté justificado y de forma fácil, sólo tendremos que reemplazar nuestro componente React que haces las veces de Root Container por un container de redux (connect) e integrarlo con las acciones y reducers que creemos.
Esta forma de trabajar nos permite tener un ciclo de desarrollo rápido, la forma en que trabajamos:
- Primero montamos los componentes sin estado (puro UI), esto nos permite modelar nuestro arbol de componentes de forma rápida, y sin que nos de "pereza" introducir cambios.
- Una vez que estamos satisfechos con el arbol de componentes, pasamos a alimentarlos con datos y de prueba o reales y a definir callbacks en nuestro componente raíz, esto nos permite interactuar con la aplicación, mostrarsela al cliente funcionando y seguir realizando cambios y ajustes de forma ligera.
- Cuando ya estamos contentos con lo que hemos implementado, toca _pasar a limpio_, dividimos esto en reducers, acciones, y selectores, todo esto siguiendo TDD.
¿ Y que pasa si estoy en mitad de un desarrollo donde ya está Redux introducido? Si por un lado tenemos claro lo que queremos hacer poder trabajar directamente con Redux, si no, podemos crear un component container intermedio y volver a desarrollar de forma ligera.
Otra cosa que te puedes plantear es usar Redux para ciertas páginas complejas y para otras simples no, no es un todo o nada.
Redux dev tools
Este plugin de navegador es una de las principales razones por las que merece la pena meter Redux en tu aplicación.
Cosas que puedes hacer:
Trae una máquina del tiempo: es decir que puedes hacer la 'moviola', darle al play, ir hacia atrás o adelante en la interacción que tuvo un usuario de tu aplicación, esto te permite poder hacer una depuración muy fina.
Te dice la diferencia y el contenido bruto tanto de tu estado como de la acción que ejecutas de cada paso.
¿Qué un desarrollador ha llegado a un caso de error que no sabe muy bien cómo reproducir? No te preocupes, puede guardar dicha secuencia a un fichero JSON, y pasártelo, cuando quieras depurarlo sólo tienes que cargarlo y ponerte paso a paso con la "moviola".
La ejecución de esta acción da un resultado interesante, sería buena idea crear una prueba unitaria con esto... muy fácil, en la pestaña de test te autogenera la prueba (en tres sabores, Mocha, Chai, Jest), sólo tienes que copiar el contenido y llevártelo a tu editor.
Tengo un caso complicado de reproducir con el interfaz de usuario, no hay problema, puedes inyectar tus propias acciones en tiempo de ejecución y ver como se porta la aplicación, también puedes eliminar acciones del flujo.
¿Hay alguna manera gráfica de ver el árbol de estado? Por supuesto, puedes mostrar un grafo, es muy interesante si le das al "play" a la moviola porque te va resaltando los nodos que van cambiando.
Una única fuente de verdad
Redux se basa en que sólo tenemos un único store, y que además el etado que almacena ese store es inmutable, si necesitamos realizar algún cambio, creamos una nueva instancia del mismo.
Para realizar cualquier cambio en un reducer, debemos de lanzar una acción y el resultado de dicha acción pasarlo a los reducers para que actualice el store.
Con esto tenemos claro de dónde podemos extraer la información, evitamos duplicidades, y también que hayan actualizaciones traidoras difíciles de depurar.
Principio de única responsabilidad
Cuando implementamos con patrones como MVVM o MVC, es complicado no caer en la mala práctica de tener controladores "gordos" que hacen más de lo que deberían.
En Redux, las piezas están muy bien definidas y además tienen que ser pequeñas por naturaleza:
- Los contenedores que conectan con los componentes React, son directos (simples mapeos).
- Las acciones sólo conectan la ejecución de una petición con enviar el resultado al reducer, el que hace la petición AJAX o un cálculo de negocio es un tercero (plain vanilla JavaScript nada que ver con el patrón ni con React).
- Los reducers son simplemente una función pura con un switch y un manejo de casos (asignación de resultados).
- Los componentes React, acaban siendo delgados, la mayoría de las veces funciones stateless que se dedican a pintar..
Plain Vanilla ES6
Si con React controlamos el UI, y con Redux el estado, ¿Qué pasa con el resto de la aplicación? Redux te empuja a que separes esto de forma clara e implementes tus módulos y librerías en Plain Vanilla ES6, de esta forma este código que generes lo puedes reaprovechar fácilmente en otros proyectos y otros frameworks.
Un ejemplo, necesitas realizar una petición AJAX, la acción le pide a un api cliente que haga la llamada y sólo trata la respuesta.
Unit Testing / TDD
Otra de las ventajas de este patrón es que se lleva muy bien con las pruebas unitarias, las piezas que utiliza son pequeñas y hacen una cosa y una sólo cosa:
- Los componentes React al quedar simplificado son más fáciles para poder añadirle pruebas unitarias.
- Los containers, probado uno, probados todos, siempre siguen el mismo mecanismo.
- Las acciones: son pequeñas y muy simple de probar.
- Los Action Creators: tiene un poco más de trabajo, pero lo mismo, son ficheros de 10 - 20 líneas de código.
- Los Reducers: no hace falta ni mocking para probarlos, son funciones puras.
Escalabilidad / Rendimiento
Cuando realizas un proyecto de tamaño medio, te encuentras con que es complicado que más de X manos toquen a la vez en él mismo, y que hay mucho riesgo de que tu código acaba altamente acoplado.
- Lo mejor de Redux es que no hace falta que un desarrollador lo conozca para ser productivo en un proyecto, puedes asignarlo a que cree la parte de vista de componentes, y sólo tiene que saber con que propiedades y acciones se tiene que conectar.
Puedes crear compartimentos estancos, de páginas / acciones, incluso reducers de página y de dominio.
Si bien tienes un sólo store, cuentas con múltiples reducers y puedes organizarlos de forma jerárquica.
Si haces las cosas bien, puedes particionar una aplicación que empieza a ser grande en aplicaciones pequeñas.
Redux es una librería que pesa muy poco (gzippeado 1.9 Kb), y además al tratar con objetos inmutables es muy fácil y óptimo decidir si un control necesita volver a ser renderizado o no.
No es Oro todo lo que reluce
Hasta aquí todo lo bueno del patrón, pero... ¿Tiene alguna debilidad? Una cosa que aprendes en esta industria es que no existe la "bala de plata", veamos sus puntos débiles:
- Tu aplicación acaba con un montón de ficheros y carpetas.
- Este patrón no es una evolución de los ya existentes, te tienes que tomar tu tiempo para aprender bien sus fundamentos.
Si no estás seguro si estás aplicando bien el patrón, aquí van unas reglas básicas de "malos olores":
¿Tus reducers están realizando llamadas AJAX (o llamadas a terceros)? Malo, tus reducers deben ser funciones puras.
¿Tus reducers están lanzando acciones? un reducer nunca debe lanzar acciones (te podrías meter en un bucle infinito).
¿Tus acciones directamente ejecutan el código de negocio? Debería siempre delegar en terceros.
¿Como manejas tus llamadas asíncronas? ¿Te suena Redux-Thunk, Redux-Saga?
¿Guardas estado de la aplicación en tus componentes o sólo estado aislado de un componente?
¿ Implementas pruebas unitarias de containers, acciones y reducers? ¿Utilizas deep freeze o aproximaciones similares para comprobar que tu store no muta?
Conclusión
Redux que te llevará su tiempo entenderlo, pero que realmente hace que tus aplicaciones sean robustas, rápidas y mantenibles.
Una vez que te hayas quedado con el funcionamiento de Redux, te saldrán un montón de dudas y áreas grises...tu camino no ha hecho nada más que comenzar, para seguir aprendiendo te recomiendo que le eches un vistazo a las siguientes librerías:
Si te ha gustado este artículo y tienes ganas de ponerte las pilas con Front End, en Lemoncode
impartimos un Máster Online, ¿Te apuntas?