Middlewares en Redux.js

Luego de ver como funciona Redux.js y como usarlo con React.js, vamos a aprender crear middlewares, estos son funciones de orden superior que nos permiten agregar funcionalidad a los Stores de Redux.

Un middleware sirve para muchas tareas diferentes, registrar datos, capturar errores, despachar promesas, cambiar rutas, etc. básicamente cualquier tarea con efectos secundarios se puede hacer en un middleware.

API

Como dije un middleware es una función, esta función va a recibir como parámetro una versión reducida del Store, con solamente los métodos dispatch y getState.

Esta función debe devolver otra función que va a recibir una función normalmente llamada next que nos sirve para llamar al siguiente middleware.

Por último devolvemos una nueva función que va a recibir la acción que se esta ejecutando. Para entenderlo bien veámoslo en código.

// nuestro middleware
function middleware(store) {
// recibimos { dispatch, getState }
return function wrapDispatch(next) {
// recibimos next
return function dispatchAndDoSomething(action) {
// recibimos la acción despachada
// acá va nuestro código
}
}
}

Esta es básicamente la estructura de un middleware, dentro de nuestra función dispatchAndDoSomething es donde vamos a colocar todo nuestro código. Otra forma escribir este código es usando arrow functions para que quede más simple y conciso.

// esto:
const middleware = store => next => action => {
// acá va nuestro código
};
// es lo mismo que esto:
const middleware = function(store) {
return function(next) {
return function(action) {
// acá va nuestro código
}
}
}

De esta forma nos quedan todos los argumentos en una línea y simplemente escribimos el código necesario.

Middleware de logging

Supongamos que queremos crear un middleware que muestre en consola cada acción despachada, el cambio en el Store y cuanto tarda en ejecutase.

const logger = store => next => action => {
// agrupamos lo que vamos a mostrar en
// consola usando el tipo de la acción
console.group(action.type);
  // mostramos el estado actual del store
console.debug('current state', store.getState());

// mostramos la acción despachada
console.debug('action', action);

// empezamos a contar cuanto se tarda en
// aplicar la acción
console.time('duration');

// pasamos la acción al store
next(action);

// terminamos de contar
console.timeEnd('duration');

// mostramos el estado nuevo
console.debug('new state', store.getState());
  // terminamos el grupo
console.groupEnd();
};

Este simple middleware registra en consola el estado del Store, la acción despachada, el nuevo estado y cuanto se tarda en realizar los cambios (perfecto para identificar problemas de performance).

Middleware de errores

Creemos otro middleware de ejemplo, hagamos uno que en caso de un error nos muestre que ocurrió.

const catchError = store => next => action => {
try {
// aplicamos la acción
next(action);
} catch (error) {
// mandamos nuestro error a algún servicio
// como Sentry o Track:js donde luego
// podamos revisarlo con detalle
errorLogger.send(error);
}
};

Este middleware super simple nos permitiría capturar cualquier error que ocurra y registrarlo ya sea en consola o en algún servicio.

Usando un middleware

Luego de creado nuestro middleware para empezar a usarlo tenemos que aplicarlo al Store al momento de crearlo.

import {
applyMiddleware,
createStore,
} from 'redux';
import reducer from './reducer';
import logger from './middlewares/logger';
import catchError from './middlewares/catch-error';
const store = createStore(
reducer,
applyMiddleware(catchError, logger)
);

De esta forma aplicamos nuestros dos middlewares al momento de crear el Store. Cabe destacar que el orden en que se pasen los middlewares importa en como se van a ejecutar, en nuestro caso primero se ejecuta catchError y luego logger de forma que si tanto logger como los reducers tienen algún error catchError lo va a registrar y si no logger va a mostrar la información en la consola.

Conclusión

Usar middlewares nos permite extender fácilmente la funcionalidad de nuestros Stores de Redux.js, sin escribir mucho código, lo que puede resultarnos muy útil en aplicaciones medianas o grandes para implementar fácilmente features que necesitemos.