Cómo implementar Redux en 24 líneas de JavaScript

90% convención, 10% biblioteca.

Redux se encuentra entre las bibliotecas de JavaScript más importantes jamás creadas. Inspirado en la técnica anterior como Flux y Elm, Redux puso la programación funcional de JavaScript en el mapa al introducir una arquitectura escalable de tres puntos simples.

Si es nuevo en Redux, considere leer los documentos oficiales primero.

Redux es principalmente Convención

Considere esta sencilla aplicación de contador que utiliza la arquitectura Redux. Si desea avanzar, consulte el repositorio de Github.

redux-counter-app-demo

El estado vive en un solo árbol

El estado de la aplicación se ve así.

const initialState = { count: 0 }; 

Las acciones declaran cambios de estado

Por convención de Redux, no modifico (muto) directamente el estado.

// DON'T do this in a Redux app state.count = 1; 

En su lugar, creo todas las acciones que el usuario puede aprovechar en la aplicación.

const actions = { increment: { type: 'INCREMENT' }, decrement: { type: 'DECREMENT' } }; 

El reductor interpreta la acción y actualiza el estado

La última pieza arquitectónica requiere un reductor, una función pura que devuelve una nueva copia de su estado en función del estado y la acción anteriores.

  • Si incrementse dispara, incremente state.count.
  • Si decrementse dispara, decrementar state.count.
const countReducer = (state = initialState, action) => { switch (action.type) { case actions.increment.type: return { count: state.count + 1 }; case actions.decrement.type: return { count: state.count - 1 }; default: return state; } }; 

No hay Redux hasta ahora

¿Notaste que aún no hemos tocado la biblioteca de Redux? Acabamos de crear algunos objetos y una función. Esto es lo que quiero decir con "principalmente convención", ¡el 90% de Redux no requiere Redux!

Implementemos Redux

Para poner esta arquitectura en uso, debemos conectarla a una tienda. Implementaremos solo una función– createStore.

Se usa así.

import { createStore } from 'redux' const store = createStore(countReducer); store.subscribe(() => { console.log(store.getState()); }); store.dispatch(actions.increment); // logs { count: 1 } store.dispatch(actions.increment); // logs { count: 2 } store.dispatch(actions.decrement); // logs { count: 1 } 

Y aquí está nuestro modelo inicial. Necesitaremos una lista de oyentes y el estado inicial proporcionado por el reductor.

const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); } 

Cada vez que alguien se suscribe a nuestra tienda, se agrega a la listenersmatriz. Es importante porque cada vez que alguien envía una acción, todos listenersdeben ser notificados en un bucle.

Llamar yourReducercon undefinedy un objeto vacío devuelve el initialStateque instalamos arriba. Esto nos da un valor adecuado para devolver cuando llamamos store.getState(). Hablando de eso, creemos ese método.

store.getState ()

Esta es una función que devuelve el último estado de la tienda. Necesitaremos esto para actualizar nuestra interfaz de usuario cada vez que el usuario haga clic en un botón.

const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); return { getState: () => currentState }; } 

store.dispatch (acción)

Esta es una función que toma actioncomo parámetro. Se alimenta de que actiony el currentStatede yourReducerconseguir un nuevo estado. Luego dispatchnotifica a todas las personas suscritas al store.

const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); return { getState: () => currentState, dispatch: (action) => { currentState = yourReducer(currentState, action); listeners.forEach((listener) => { listener(); }); } }; }; 

store.subscribe (oyente)

Esta es una función que le permite ser notificado cuando la tienda recibe una acción. Es bueno usarla store.getState()aquí para obtener su último estado y actualizar su interfaz de usuario.

const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); return { getState: () => currentState, dispatch: (action) => { currentState = yourReducer(currentState, action); listeners.forEach((listener) => { listener(); }); }, subscribe: (newListener) => { listeners.push(newListener); const unsubscribe = () => { listeners = listeners.filter((l) => l !== newListener); }; return unsubscribe; } }; }; 

subscribedevuelve una función llamada a la unsubscribeque puede llamar cuando ya no esté interesado en escuchar las actualizaciones de la tienda.

Todos juntos ahora

Conectemos esto a nuestros botones y veamos el código fuente final.

// simplified createStore function const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); return { getState: () => currentState, dispatch: (action) => { currentState = yourReducer(currentState, action); listeners.forEach((listener) => { listener(); }); }, subscribe: (newListener) => { listeners.push(newListener); const unsubscribe = () => { listeners = listeners.filter((l) => l !== newListener); }; return unsubscribe; } }; }; // Redux architecture pieces const initialState = { count: 0 }; const actions = { increment: { type: 'INCREMENT' }, decrement: { type: 'DECREMENT' } }; const countReducer = (state = initialState, action) => { switch (action.type) { case actions.increment.type: return { count: state.count + 1 }; case actions.decrement.type: return { count: state.count - 1 }; default: return state; } }; const store = createStore(countReducer); // DOM elements const incrementButton = document.querySelector('.increment'); const decrementButton = document.querySelector('.decrement'); // Wire click events to actions incrementButton.addEventListener('click', () => { store.dispatch(actions.increment); }); decrementButton.addEventListener('click', () => { store.dispatch(actions.decrement); }); // Initialize UI display const counterDisplay = document.querySelector('h1'); counterDisplay.innerHTML = parseInt(initialState.count); // Update UI when an action fires store.subscribe(() => { const state = store.getState(); counterDisplay.innerHTML = parseInt(state.count); }); 

Y una vez más, aquí está nuestra interfaz de usuario final.

redux-counter-app-demo

Si está interesado en el HTML / CSS que utilicé, ¡aquí está el repositorio de GitHub nuevamente!

¿Quieres coaching gratuito?

Si desea programar una llamada gratuita para discutir preguntas de desarrollo de Front-End con respecto al código, entrevistas, carrera o cualquier otra cosa, sígame en Twitter y envíeme un mensaje de correo electrónico.

Después de eso, si disfruta de nuestra primera reunión, ¡podemos discutir un entrenamiento continuo para ayudarlo a alcanzar sus objetivos de desarrollo de Front-End!

Use sus contribuciones

Si está codificando todos los días, especialmente si se está comprometiendo con GitHub, ¿no sería genial usar ese mapa de contribución para que todos lo vean?

¡Gitmerch.com te permite imprimir una camiseta de tu mapa de contribución de GitHub! Utilice el código, Yazeed , al finalizar la compra para obtener un descuento.

git-merch-captura de pantalla-1-1

git-merch-captura de pantalla-2-1

Gracias por leer

Para obtener más contenido como este, visite //yazeedb.com.

¡Hasta la proxima vez!