- Redux centraliza y controla el estado de tu aplicación con reglas claras y predecibles
- Permite depuración avanzada, escalabilidad y arquitectura robusta para grandes proyectos
- Su ecosistema facilita integraciones con React, otros frameworks y herramientas de desarrollo
Redux es uno de los nombres que más suenan al hablar de gestión del estado en aplicaciones JavaScript modernas. Aunque muchas personas lo asocian directamente al ecosistema de React, la realidad es que Redux puede trabajar perfectamente con cualquier framework o incluso en proyectos JavaScript puros gracias a su filosofía agnóstica y sencilla.
Entender Redux y saber cuándo, cómo y por qué usarlo puede marcar la diferencia entre una app caótica y una mantenible, robusta y fácil de depurar. Si has escuchado hablar de Flux, reducers, actions o de la tan temida “gestión de estado”, aquí te desgranamos todo lo que necesitas saber, combinando lo mejor de la documentación, experiencias de la comunidad y casos prácticos, pero explicado con palabras claras y ejemplos frescos.
¿Qué es Redux y por qué existe?
Redux es una biblioteca JavaScript pensada para gestionar el estado de aplicaciones de una forma centralizada y predecible. Se basa en conceptos de la programación funcional como la inmutabilidad y el uso de funciones puras, buscando que cualquier cambio que ocurra en la aplicación sea fácil de entender, rastrear y depurar.
Fue creada en 2015 por Dan Abramov y Andrew Clark, inspirándose en Flux (de Facebook) y en Elm, con el objetivo de simplificar la gestión del estado global y evitar los dolores de cabeza derivados de la compartición de datos entre componentes en aplicaciones cada vez más grandes y complejas.
Redux se hizo extremadamente popular gracias a React, pero su arquitectura no tiene ninguna dependencia obligatoria con React, de ahí que se catalogue como «framework agnostic». Se puede emplear con Angular, Vue, Svelte, o sin framework, en proyectos vanilla JS.
¿Qué problemática resuelve Redux?
En cualquier aplicación frontend, el concepto de «estado» representa todos los datos (variables, valores, flags, respuestas de APIs, rutas activas, selectores, etc.) necesarios para que la interfaz funcione correctamente en cada momento. En aplicaciones pequeñas, manejar este estado no suele dar problemas porque normalmente se limita a variables locales de cada componente o widget. Sin embargo, cuando la aplicación crece y empiezas a necesitar que diferentes partes de la app compartan o dependan de información común, la gestión del estado se complica rápidamente con prop drilling, estados duplicados, y lógica impredecible.
En estos escenarios, se vuelve necesario un sistema que:
- Centralice todo el estado relevante en un lugar único y accesible.
- Imponga reglas claras sobre cómo y cuándo se puede modificar ese estado, evitando actualizaciones accidentales.
- Permita rastrear con facilidad las acciones que han provocado cada cambio, ayudando a depurar y a entender la evolución del estado.
- Facilite la extensión y el mantenimiento de la aplicación, permitiendo añadir nuevas funcionalidades sin miedo a romper dependencias ocultas.
Redux surge precisamente para cubrir estas necesidades, imponiendo una arquitectura robusta y sencilla donde el estado es «la única fuente de verdad» y solo puede alterarse de maneras bien definidas y previsibles.
Los tres principios clave de Redux
- El estado global está contenido en un único objeto llamado store: Todo lo que tu aplicación necesita saber debe estar ahí. No hay estados repartidos entre componentes que puedan desincronizarse.
- El estado sólo puede cambiarse emitiendo acciones: Nadie puede modificar el estado directamente. Solo se lanzan acciones que describen qué ha pasado y, opcionalmente, aportan datos extra (payload).
- Los cambios de estado se realizan a través de funciones puras denominadas reducers: Estas funciones reciben el estado anterior y una acción, y devuelven el nuevo estado. No deben tener efectos secundarios ni modificar el estado original, sino retornar uno nuevo con los cambios.
Estructura básica de Redux: cómo funciona
El ciclo de vida de los datos en Redux sigue un flujo unidireccional extremadamente predecible:
- Un componente o proceso del frontend (por ejemplo, al hacer click, escribir, recibir una solicitud de API…) lanza una acción, que es simplemente un objeto con al menos una propiedad type y, opcionalmente, un payload extra con datos relevantes.
- La acción viaja hasta el store, que la entrega junto al estado actual a los reducers.
- Los reducers computan el nuevo estado en función del antiguo estado y la acción recibida, SIEMPRE devolviendo un nuevo objeto (nunca modificando el anterior).
- El store actualiza el estado y notifica a todos los componentes suscritos, que pueden así re-renderizarse con los nuevos datos.
Este flujo tan simple permite la aparición de características avanzadas como el «time travel», la reproducción de errores exactos partiendo de historiales de acciones, y una depuración prácticamente automática.
Ejemplo visual y de código
Supón que tienes una app donde el usuario puede sumar y restar a un contador. Podrían existir acciones { type: 'INCREMENTAR', payload: 1 }
y { type: 'DECREMENTAR', payload: 1 }
. Un reducer típico sería:
function contadorReducer(state = { valor: 0 }, action) {
switch(action.type) {
case 'INCREMENTAR':
return { ...state, valor: state.valor + action.payload };
case 'DECREMENTAR':
return { ...state, valor: state.valor - action.payload };
default:
return state;
}
}
No importa qué framework uses; la filosofía es siempre la misma.
Elementos fundamentales en Redux
-
- Store: Es el objeto central. Solo hay uno y contiene todo el estado de la aplicación. Expone métodos como
getState()
(devuelve el estado actual),dispatch(action)
(lanza una acción), ysubscribe(listener)
(permite ejecutar una función cada vez que cambia el estado). Las librerías JavaScript más relevantes pueden ser útiles para ampliar sus funcionalidades. - Acciones: Objetos planos. Siempre tienen una propiedad type (string) y pueden llevar datos adicionales bajo payload u otros campos.
- Action creators: Funciones que construyen acciones. Facilitan evitar errores y garantizan consistencia. Ejemplo:
- Store: Es el objeto central. Solo hay uno y contiene todo el estado de la aplicación. Expone métodos como
function incrementar(valor) {
return { type: 'INCREMENTAR', payload: valor };
}
- Reducers: Funciones puras. Reciben el estado y la acción, devuelven el estado resultante. Nunca deben modificar ni el estado ni la acción recibida directamente.
- Middleware: Son «piezas intermedias» que se cuelan entre el dispatch de la acción y el momento en que llega al reducer. Ideales para logging, llamadas a APIs, gestión de errores o side-effects asíncronos. Ejemplos famosos son
redux-thunk
yredux-saga
. Frameworks JavaScript relacionados facilitan su integración.
¿Por qué elegir Redux y en qué escenarios destaca?
La mayor fortaleza de Redux es dar control y claridad sobre el flujo de datos en aplicaciones grandes. Cuando sólo tienes unos pocos componentes, probablemente te bastará con estados locales o la Context API de React. Pero si tu proyecto exige:
- Comunicación frecuente entre partes distantes de la interfaz (por ejemplo, una barra de usuario que debe reflejar cambios hechos desde cualquier rincón de la app).
- Reproducción exacta de errores y depuración avanzada.
- Soporte para herramientas de desarrollo como Redux DevTools, que permiten analizar en tiempo real el historial de acciones y los cambios de estado.
- Gestión de flujo asíncrono organizada (peticiones a servidores, websockets, etc.).
Entonces, Redux puede ahorrarte innumerables frustraciones.
¡No solo para React!
La integración con otros frameworks es directa o mediante adaptadores. Angular, Vue, Ember, Svelte… todos pueden beneficiarse de Redux para centralizar el estado si así lo requieren. Además, hay utilidades como redux-immutable
o redux-duck
que facilitan la modularidad y la integración con colecciones inmutables. también cubren estos aspectos.
¿Redux o useReducer/Context?
Desde que React introdujo Hooks, muchos desarrolladores se preguntan si useReducer junto con Context API no es suficiente. En aplicaciones sencillas, sí es suficiente. Pero a medida que el estado compartido cobra importancia, las ventajas de Redux se hacen notar: herramientas de depuración, modularidad, middleware, ecosistema maduro y patrones muy estudiados.
La estructura de un proyecto Redux: buenas prácticas
Una estructura clara es vital para proyectos grandes. Suele recomendarse separar carpetas para:
- actions/ (creadores de acciones, tipos…)
- reducers/
- store/
- components/ (presentacionales, sin conexión directa a Redux)
- containers/ (componentes conectados a Redux, responsables de pasar datos y acciones a componentes de presentación)
Además, el patrón «ducks» permite agrupar reducers, actions y tipos relacionados en un solo archivo por funcionalidad, lo que puede facilitar el mantenimiento en muchos equipos.
Trabajando con Middleware y acciones asíncronas
La vida real no es síncrona: necesitas cargar datos, interactuar con APIs y manejar errores. Aquí entra el middleware. Dos de los más populares son:
- redux-thunk: Permite despachar funciones asíncronas y hacer dispatch de acciones cuando la petición finaliza.
- redux-saga: Permite describir flujos complejos de side-effects de forma más declarativa usando generadores.
Con ellos puedes, por ejemplo, lanzar una petición a un API, esperar su respuesta y, sólo entonces, actualizar el store con los datos recibidos o con un error. Así, integras la lógica business con el ciclo de Redux.
Testing y depuración en Redux
Gracias al uso de funciones puras y objetos planos, Redux es especialmente amigable con las pruebas unitarias. Probar un reducer o un action creator es trivial: les das entradas conocidas, y debes obtener salidas predecibles. Los middlewares pueden requerir pruebas más elaboradas, pero su diseño también lo facilita.
Por otro lado, Redux DevTools proporciona una experiencia de depuración incomparable: puedes retroceder o avanzar por todas las acciones disparadas, ver el estado exacto en cada momento e incluso «reproducir» bugs partiendo de un historial de acciones grabado.
Ejemplo práctico: una app sencilla de tareas
Para ver Redux en acción, aquí tienes un ejemplo extremadamente simplificado (sin frameworks) de cómo crear la lógica para una lista de tareas:
import { createStore } from 'redux';
// Estado inicial
const estadoInicial = { tareas: [] };
// Reducer
function tareasReducer(state = estadoInicial, action) {
switch(action.type) {
case 'ANADIR_TAREA':
return { ...state, tareas: };
case 'ELIMINAR_TAREA':
return { ...state, tareas: state.tareas.filter(t => t.id !== action.payload) };
default:
return state;
}
}
// Store
const store = createStore(tareasReducer);
// Action creators
function anadirTarea(texto) {
return { type: 'ANADIR_TAREA', payload: { id: Date.now(), texto } };
}
function eliminarTarea(id) {
return { type: 'ELIMINAR_TAREA', payload: id };
}
// Suscripción
store.subscribe(() => {
console.log('Tareas actuales:', store.getState().tareas);
});
// Despachamos algunas acciones
store.dispatch(anadirTarea('Aprender Redux'));
store.dispatch(anadirTarea('Dominar JavaScript'));
store.dispatch(eliminarTarea(store.getState().tareas.id));
Esto demuestra la simplicidad de la arquitectura y lo predecible que resulta intervenir en el flujo de datos incluso sin frameworks alrededor.
Integración con React: React-Redux
Para trabajar con Redux en aplicaciones React, lo habitual es usar react-redux, un paquete que proporciona el componente Provider
(para inyectar el store en todo el árbol) y los hooks useSelector
y useDispatch
para acceder al estado y despachar acciones desde cualquier componente funcional.
Esto evita acoplar manualmente el store y simplifica mucho el código, facilitando la separación entre componentes de presentación (sin lógica de store) y contenedores (conectados a Redux).
Tabla de Contenidos
- ¿Qué es Redux y por qué existe?
- ¿Qué problemática resuelve Redux?
- Los tres principios clave de Redux
- Estructura básica de Redux: cómo funciona
- Elementos fundamentales en Redux
- ¿Por qué elegir Redux y en qué escenarios destaca?
- ¡No solo para React!
- ¿Redux o useReducer/Context?
- La estructura de un proyecto Redux: buenas prácticas
- Trabajando con Middleware y acciones asíncronas
- Testing y depuración en Redux
- Ejemplo práctico: una app sencilla de tareas
- Integración con React: React-Redux