Criando middlewares personalizados no Redux

Resumen

Los middlewares en Redux son fragmentos de código que se ejecutan entre el momento en que se dispara una acción y el instante en que llega al reducer. Aprenderás a crear middlewares personalizados desde cero, combinarlos con enhancers y aplicarlos a tu store para registrar acciones, modificar payloads y ampliar las capacidades de tu aplicación.

¿Qué es un middleware en Redux y para qué sirve?

Un middleware es una pieza de código que intercepta cada acción antes de que llegue al reducer. Esa intercepción te abre un abanico enorme de posibilidades: registrar errores, enviarlos a gestores como Sentry, hacer fetch de datos o depurar tu aplicación con detalle.

¿Para qué se usa un middleware en Redux? Para ejecutar lógica entre el dispatch de una acción y su llegada al reducer. Te permite loggear, modificar el payload, manejar efectos secundarios o conectar con servicios externos.

Antes de escribir código, conviene tener claros tres términos que aparecen una y otra vez cuando trabajas con el store [01:00].

¿Qué diferencia hay entre store creator, enhancer y compose?

  • Store creator: la función que crea tu store de Redux.
  • Enhancer: una función de orden superior que recibe un store creator y devuelve una versión potenciada con superpoderes. La línea que añadiste para usar Redux DevTools es justamente un enhancer porque amplía la funcionalidad del estado y te permite inspeccionarlo desde el navegador.
  • Compose: utilidad de programación funcional que combina funciones de derecha a izquierda. La usarás cuando necesites aplicar varios enhancers a la vez.

Con estas piezas claras, ya puedes pasar a la práctica.

¿Cómo crear un middleware logger personalizado?

El primer middleware que vas a construir registra cada acción disparada en la consola. Para empezar, crea una carpeta middlewares dentro de src y un archivo index.js [02:30].

La estructura de un middleware en Redux es muy específica. Se trata de una función curried, es decir, una función que retorna otra función, y así sucesivamente:

js const logger = (store) => (next) => (action) => { console.log(action); next(action); };

export default logger;

Cada parámetro tiene un rol concreto:

  • store: es el store de tu aplicación.
  • next: la función que despacha la acción al reducer. Si no la llamas, la acción nunca llega.
  • action: la información de lo que está ocurriendo.

¿Qué pasa si no llamo a next dentro de un middleware? La acción se queda atrapada en el middleware y nunca llega al reducer, así que el estado no cambia.

¿Cómo combinar varios enhancers con compose y applyMiddleware?

En tu index.js principal, necesitas combinar el enhancer de Redux DevTools con tu nuevo middleware. Para eso, importa compose y applyMiddleware desde Redux:

js import { createStore, compose, applyMiddleware } from 'redux'; import logger from './middlewares';

const composedEnhancers = compose( window.REDUX_DEVTOOLS_EXTENSION_COMPOSE || compose, applyMiddleware(logger) );

const store = createStore(reducer, composedEnhancers);

No puedes pasar el logger directamente a compose, porque la estructura de parámetros curried no se respetaría. Por eso applyMiddleware envuelve el middleware y lo entrega a compose con el formato correcto [05:20].

Al abrir la consola del navegador verás cada acción con su tipo, su payload y su timestamp. Confirmas así que el middleware funciona.

¿Cómo modificar el payload de una acción desde un middleware?

Un middleware no solo observa: también puede transformar la información antes de que llegue al reducer. Imagina que, sin importar qué Pokémon devuelva la API, quieres añadir uno personalizado al inicio de la lista [07:10].

Creas un middleware llamado featuring con la misma estructura curried:

js const featuring = (store) => (next) => (actionInfo) => { const modifiedPokemons = [ { name: 'Eddie' }, ...actionInfo.action.payload, ];

const updatedAction = { ...actionInfo, action: { ...actionInfo.action, payload: modifiedPokemons, }, };

next(updatedAction); };

La lógica es directa: tomas todo lo que viene en actionInfo.action.payload, lo desestructuras con el operador spread, y antepones tu Pokémon personalizado. Después construyes una updatedAction que conserva el resto de propiedades intactas y reemplaza solo el payload.

¿Cómo aplicar varios middlewares al mismo store?

applyMiddleware acepta tantos middlewares como quieras pasarle. Solo necesitas listarlos en el orden en que deben ejecutarse:

js applyMiddleware(logger, featuring)

¿Puedo usar más de un middleware en Redux? Sí. applyMiddleware recibe múltiples middlewares como argumentos y los ejecuta en cadena, en el orden en que los declares.

Al recargar el navegador, verás a Eddie como primer Pokémon de la lista. Y si pides un offset a la API para traer los siguientes 151 Pokémon, toda la lista cambia menos Eddie, porque ese se inyecta dentro del middleware antes de llegar al reducer [10:15].

Este ejemplo es sencillo, pero abre la puerta a casos reales: enriquecer respuestas, filtrar datos sensibles, transformar formatos o disparar lógica condicional.

¿Qué middleware personalizado se te ocurre añadir a este proyecto? Cuéntalo en los comentarios y comparte tu implementación.