¿Cómo empezar con Redux en 7 minutos?

Redux es un arquitectura que te permite controlar el flujo de datos de una aplicación web moderna de manera más eficiente, este flujo de los datos funciona en una sola dirección, por esta única dirección es mucho más fácil controlar las mutaciones y operaciones asíncronas.Esta arquitectura es parecida a flux, de hecho flux fue una fuente de inspiración para la creación de Redux.

En el estado de una aplicación, principalmente en aplicaciones SPA, esto es muy importante, porque las aplicaciones web modernas tienen gran complejidad de operaciones asíncronas y sus relaciones entre sus componentes. Este tipo de aplicaciones necesitan controlar peticiones a servidores, obtener datos y controlar el estado en el cliente, aun cuando estos datos no han sido guardados en el servidor. Estas aplicaciones modernas requieren de un control complejo y por eso nuevas arquitecturas como la de Redux y flux  nacen para hacer el desarrollo de estas aplicaciones más productivo.

Las mutaciones de objetos son difíciles de manejar y más a escalas medianas y grandes, se comienza a perder el control de los datos, este flujo de datos generan comportamiento no deseados y mala información desplegada al usuario, código que es muy difícil de mantener y cosas aún más elevadas como tu salud, causa de estrés por el esfuerzo en arreglar estas inconsistencias, te hace perder tiempo para mantenerlo funcionando correctamente y aun encima de todo afectas a terceras personas, los usuarios, así es, ellos obtienen información incorrecta y pierden su preciado tiempo. ¿Qué decir de las operaciones asíncronas? Bueno prácticamente pasa lo mismo o aún peor, porque en una operación asíncrona no sabes cuando obtendrás el resultado y además normalmente vienen acompañadas con el deseo de hacer modificaciones al estado de la aplicación.

Un ejemplo común es el control de los datos de tu aplicación con cualquier frawework front-end de componentes. Primero veamos el flujo de datos sin Redux:

Flujo de datos sin Redux, por todas direcciones, un relajo, difícil de mantener y muchos errores

Ahora veamos el mismo flujo de datos con Redux:

Flujo de datos con Redux, una sola dirección, un control mas eficiente y menos errores

Flujo de datos con Redux, una sola dirección, un control mas eficiente y menos errores

Tres elementos importantes que forman el estado de tu aplicación en Redux

Store. El store es un objeto donde guardas toda la información del estado o state de tu aplicación, es como el modelo de una aplicación, con la excepción que no lo puedes modificar directamente, de hecho cuando queremos modificar el store realmente estamos creando uno nuevo con los datos actualizados, veremos esto más adelante.

Actions. Son el medio por el cual indicas que quieres realizar una modificación en el estado de la aplicación, es como un mensaje o notificación breve. Solo enviando la información necesaria para realizar el cambio.

Reducers. Son las funciones que realizan el cambio en el store, lo que hacen internamente es crear un nuevo estado con la información actualizada, de tal manera que los cambios se reflejan inmediatamente en la aplicación. Los reducers son funciones puras, es decir, sin efectos colaterales, no mutan el estado, sino que crean uno con información nueva.

Redux se basa en tres principios

Un único Store para representar el estado de toda la aplicación. Tener una sola fuente de datos para toda tu aplicación permite tener centralizada la información, evita problemas de comunicación entre componentes para desplegar los datos, fácil de depurar y menos tiempo agregando funcionalidad  o detectando errores.

No modificaciones en el Estado o State. Esto permite tener el control de cambios y evitar un relajo entre los diferentes componentes de tu aplicación, ni los componentes, ni peticiones ajax pueden modificar directamente el Estado (state) de tu aplicación, esto quiere decir que si quieres actualizar tu estado, debes  hacerlo a través de actions, de esta manera redux se encarga de realizar las actualizaciones de manera estricta y en el orden que le corresponden.

Los cambios solo se hacen con funciones puras. Al realizar los cambios con funciones puras, lo que realmente se hace es crear un nuevo objeto con la información actualizada, estas funciones puras son los reducers y no mutan el estado, al no mutar el estado, se evitan problemas de control, datos incorrectos, mal comportamiento y errores, también permite que la depuración sea más fácil. Puedes dividir los reducer en varios archivos diferentes y las pruebas unitarias son fáciles de implementar.

A diferencia de otras arquitecturas como MVC (Modelo, vista, controlador), donde los cambios pueden existir en ambas direcciones, es decir, la vista puede cambiar el estado, el modelo lo podría modificar y también el controlador, todos estos cambios necesitan estar sincronizados en varias partes de una aplicación para evitar inconsistencias, lamentablemente este problema de inconsistencia se vuelve muy difícil y tedioso de resolver.

Mi primer State con Redux

Veamos un ejemplo concreto para saber de que estamos hablando, empecemos con el más sencillo, el control de un contador:

Ejemplo de contador utiizando EcmaScript 5

Si quieres probar este ejemplo en tu maquina recuerda insertar o importar la librería Redux. Ahora corremos el pen (clic en Run Pen), podremos ver el funcionamiento de Redux, cada vez que se da clic sobre los botones de incrementar y decrementar, el contador se incrementa y decrementa según corresponda.

Si ahora nos vamos al código JS, podremos ver que solo tenemos una función llamada counter, esta función es un reducer y es una función pura, sin efectos colaterales.

Luego vemos que como parámetros recibe un state y un action, cuando creamos nuestro Store con Redux, estas funciones reducer son utilizadas para modificar el State. Normalmente el state, reducer y action son definidos en archivos diferentes, y pueden ser organizados en varios archivos y carpetas dependiendo de la necesidad de la aplicación, toda esta organización tiene la finalidad de tener un código limpio y separar funcionalidades. En este caso sencillo no es necesario, en un solo archivo tenemos todo.

Nuestro state es solo un valor numérico, el cual se va a incrementar o decrementar con los botones que están en la página, normalmente el state es un objeto literal o un array literal, pero en nuestro caso solo necesitamos un numero. Dado que necesitamos definir ese numero, en las primeras líneas de nuestro reducer revisamos si no está definido y sino lo esta, le asignamos el valor 0. Su definición podría estar en otro archivo, pero para este ejemplo no es necesario.

if(typeof state === 'undefined') {
  state = 0;
}

Si seguimos revisando nuestro código, veremos unos cuantas condiciones que revisa el valor del parámetro action y dependiendo de la acción, realizan una operación, en nuestro caso son las operaciones INCREMENTARo DECREMENTAR.

Los actions no son mas que objetos literales como los siguientes:

const incrementar = { type: 'INCREMENTAR' }
const decrementar = { type: 'DECREMENTAR' }

Los reducer revisan esos actions, en nuestro ejemplo:

if (action.type === 'INCREMENTAR') {
  return state +  1;
}

if (action.type === 'DECREMENTAR') {
  return state - 1;
}

Con todo esto ya tenemos nuestro State y su estado inicial en caso de no estar definido,también nuestra primer reducer, counter, y nuestros primeros actions INCREMENTARy DECREMENTAR.

Es el momento de crear nuestro Store, utilizando la librería redux esto es muy fácil:

const store = Redux.createStore(counter);

Con la anterior línea, Redux crea un Store para controlar el estado de nuestra aplicación. Internamente un Store es un Patrón observadorque utiliza un Singleton para el State y expone los siguientes métodos principales:

  • store.getState()
  • store.subscribe(listener)
  • store.dispatch(action)

store.getState()te permite obtener el estado actual de tu aplicación.

store.subscribe(listener)te permite ejecutar la función listener (u observador), cada vez que el store es actualizado.

store.dispatch(action)te permite actualizar el estado, esta modificación no es directa, siempre se realiza a través de un action y se ejecuta con un reducer.

Luego creamos una función render, la cual es creada para que se ejecute cada vez que el State de tu aplicación haya sido actualizado.

Aquí nuestro contenido html:

<button id="incrementar">Incrementar</button>
<button id="decrementar">Decrementar</button>
<div id="state"></div>

Nuestra función render

function render () {
  document.querySelector('#state').innerText = store.getState(); 
}

Ahora invocamos inmediatamente esta función para obtener el State inicial, es decir, el valor en la página.

render()

Aquí te preguntaras, ¿Como es posible que esa línea imprima 0 en la pantalla?, pues internamente el store invoca un dispatch con un action vacio, store.dispatch({})y al no encontrar ninguna acción, entonces regresa el estado inicial, ¿Recuerdas esta condicion if(typeof state === 'undefined')en nuestro único reducer reduce? Esto actualiza inmediatamente el State.

Luego nos subscribimos a store para escuchar u observar el State cada vez que se actualice y entonces poder ejecutar la función render().

store.subscribe(render);

Esta línea permite que cuando demos clic en los botones de incrementar y decrementar, se imprima el nuevo valor en la página.

Y por último agregamos dos listeners correspondientes a los botones de incrementar y decrementar, cada botón realiza una invocacion dispatchpara modificar el estado.

<button id="incrementar">Incrementar</button>
<button id="decrementar">Decrementar</button>
<div id="state"></div>
document.querySelector('#incrementar').addEventListener('click', function () {
  store.dispatch(incrementar);
});
document.querySelector('#decrementar').addEventListener('click', function () {
  store.dispatch(decrementar);
});

Al fin tenemos el flujo de datos de nuestro sencillo ejemplo usando Redux, en general los componentes que forman a una aplicación que utiliza Redux son los siguientes:

Podemos notar el flujo en una sola dirección desde la vista, a través con los actions con dispatch(action), luego  los reducers como counter(prevState, action)para actualizar el store y este último manda la información a través de subscribe(render)y getState().

Redux como ya se menciono en párrafos anteriores, nos proporciona:

  • El conocimiento en todo momento del estado de nuestra aplicación y en cualquier parte de la aplicación con mucha facilidad.
  • Fácil organización, ya sabes, actions, reducer, store y view con cambios en una sola dirección, además puedes separar tus actions, reducer y el store en varios archivos.
  • Fácil de mantener, debido a su organización y su único store, mantener su funcionamiento y agregar nuevas funcionalidades es muy sencillo.
  • Tiene muy buena documentación, su comunidad es grande y tiene su propia herramienta para debuguear
  • Todos  los puntos anteriores hacen que sea fácil de hacer pruebas unitarias.
  • Y lo más importante, te ahorra tiempo, dinero y esfuerzo xD.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

This site uses Akismet to reduce spam. Learn how your comment data is processed.