Por qué Redux Toolkit: el fin del boilerplate
Redux dio disciplina y arrastró fama de ceremonia interminable: constantes de tipos, action creators a mano, reducers con switch, actualizaciones inmutables por spread y un store cableado pieza a pieza. Esta lección diseca ese impuesto, muestra que era complejidad accidental y no esencial, y explica por qué Redux Toolkit es hoy la forma oficial y recomendada de escribir Redux. RTK no reemplaza el modelo de Redux: lo conserva entero y elimina el ritual que lo rodeaba, colapsando cuatro archivos en un slice. Cierra con la pregunta incómoda de 2026: cuándo necesitas Redux siquiera, y cuándo el estado de servidor pide RTK Query o una cache antes que un store global.
Redux se ganó dos reputaciones a la vez: la de imponer una disciplina que salva proyectos grandes —un flujo de datos único, cambios explícitos, historial inspeccionable— y la de exigir una ceremonia que agota, tres archivos y cincuenta líneas para incrementar un contador. Durante años esas dos reputaciones parecían inseparables, como si el rigor solo se pagara con verbosidad. Redux Toolkit demostró que no: que casi todo lo que dolía de Redux era ritual accidental, no la esencia del modelo. RTK no es una librería más del ecosistema ni una alternativa a Redux; es Redux, el mismo store y el mismo reducer puro de siempre, envuelto en una API que borra el boilerplate y deja a la vista solo la idea. En 2026 escribir Redux a mano no es una opción legítima con desventajas, es sencillamente un error que la documentación oficial desaconseja.
- Nombrar las piezas del Redux clásico y medir el impuesto de ceremonia que cobraba cada una.
- Distinguir la complejidad esencial del modelo de Redux de la accidental que RTK elimina.
- Ver cómo
createSlice,configureStoreycreateAsyncThunkcolapsan cuatro archivos en uno. - Situar RTK en el ecosistema de 2026 y saber cuándo el problema ni siquiera pide Redux.
El impuesto del Redux clásico
El Redux original te pedía escribir a mano cuatro capas para cada porción de estado. Primero, constantes de tipo de acción, cadenas mágicas como contador/incrementar que había que declarar, exportar e importar sin errata. Segundo, action creators, funciones triviales que solo envuelven un objeto { type, payload }. Tercero, un reducer con un switch gigante que crecía con cada caso y cuyo default no podías olvidar. Cuarto, la actualización inmutable a mano: nunca mutar el estado, siempre devolver copias con spread anidado, algo que en objetos profundos se volvía ilegible y propenso a bugs silenciosos. A eso sumabas el cableado del store —createStore, applyMiddleware, composeWithDevTools— y la instalación manual de redux-thunk para cualquier asincronía. Nada de esto era difícil; era repetitivo, y la repetición es donde se esconden las erratas.
// Redux clasico: cuatro piezas para un contador
// 1) constantes de tipo
const INCREMENTAR = "contador/incrementar";
const SUMAR = "contador/sumar";
// 2) action creators a mano
const incrementar = () => ({ type: INCREMENTAR });
const sumar = (n: number) => ({ type: SUMAR, payload: n });
// 3) reducer con switch y 4) actualizacion inmutable por spread
interface Estado { valor: number }
const inicial: Estado = { valor: 0 };
function contador(estado = inicial, accion: any): Estado {
switch (accion.type) {
case INCREMENTAR:
return { ...estado, valor: estado.valor + 1 };
case SUMAR:
return { ...estado, valor: estado.valor + accion.payload };
default:
return estado;
}
}
Qué colapsa Redux Toolkit
createSlice toma un nombre, un estado inicial y un objeto de reducers, y a cambio te devuelve el reducer, los action creators y los tipos de acción, todo generado y coherente entre sí. Los nombres de acción se derivan del nombre del slice más la clave del reducer, así que contador/incrementar ya no es una cadena que escribes sino una que RTK deriva. Y gracias a Immer, dentro de cada reducer puedes escribir lo que parece una mutación —estado.valor += 1— sabiendo que por debajo se traduce a una actualización inmutable. Las cuatro capas del ejemplo anterior se colapsan en una declaración legible.
import { createSlice, type PayloadAction } from "@reduxjs/toolkit";
const contadorSlice = createSlice({
name: "contador",
initialState: { valor: 0 },
reducers: {
incrementar: (estado) => {
estado.valor += 1; // Immer convierte esta "mutacion" en algo inmutable
},
sumar: (estado, accion: PayloadAction<number>) => {
estado.valor += accion.payload;
},
},
});
export const { incrementar, sumar } = contadorSlice.actions;
export default contadorSlice.reducer;
El store se monta con una sola llamada, y con ella llegan gratis DevTools, redux-thunk y las comprobaciones de inmutabilidad y serializabilidad en desarrollo.
import { configureStore } from "@reduxjs/toolkit";
import contador from "./contadorSlice";
export const store = configureStore({ reducer: { contador } });
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Redux a mano
Constantes, action creators, switch, spread inmutable y cableado del store con thunk y DevTools. Cuatro archivos y muchas erratas posibles por cada porción de estado.
Redux Toolkit
Un slice declara estado, acciones y reducer a la vez; configureStore cablea todo con buenos defaults. El mismo modelo, sin el ritual que lo escondía.
Lo que RTK trae, además del slice
createSlice, configureStore y createAsyncThunk son la primera capa, pero RTK incluye más herramientas que el Redux clásico obligaba a reinventar o a instalar por separado. createEntityAdapter normaliza colecciones —las guarda como un diccionario por id más un array de ids ordenados— y regala reducers y selectores hechos para insertar, actualizar y borrar sin recorrer arrays a mano. Y createSelector, reexportado de Reselect, construye selectores memoizados que solo recalculan una derivación cara cuando cambian sus entradas, en vez de en cada render.
import { createEntityAdapter, createSelector } from "@reduxjs/toolkit";
const adaptador = createEntityAdapter<Post>();
// estado normalizado: ids ordenados mas un diccionario entities por id
const selectores = adaptador.getSelectors();
// derivacion memoizada: solo recalcula si cambian los posts
const seleccionarTitulos = createSelector(
selectores.selectAll,
(posts) => posts.map((p) => p.titulo),
);
createEntityAdapter y createSelector atacan dos costes que crecen con la app: buscar y actualizar dentro de arrays largos, y recalcular derivaciones caras en cada render. Adoptarlos tarde obliga a reescribir selectores y reducers ya asentados, así que conviene normalizar las colecciones desde el principio y memoizar toda derivación no trivial. Son parte del “por qué RTK” tanto como el slice: el Redux clásico también los necesitaba, solo que te dejaba montarlos a mano y equivocarte.
RTK es Redux, y a veces no necesitas Redux
La segunda lección de RTK es más sutil que la primera. Borrar el boilerplate no solo hace Redux agradable; deja a la vista una decisión que la ceremonia ocultaba: la de si tu estado pertenece a Redux siquiera. Buena parte de lo que la gente metía en un store nunca fue estado de cliente, sino una copia de datos del servidor que se quedaba obsoleta. En 2026 eso se resuelve con RTK Query o TanStack Query, que son caches, no stores. El estado puramente local vive mejor en useState o useReducer. Solo lo que queda —estado global de cliente que de verdad se comparte entre partes lejanas de la app— justifica un store de Redux.
flowchart TD A[que clase de estado es] -->|de servidor| B[RTK Query o TanStack Query] A -->|local de un componente| C[useState o useReducer] A -->|global de cliente compartido| D[Redux Toolkit] D --> E[createSlice mas configureStore] style B fill:#a6e3a1,color:#11111b style C fill:#89b4fa,color:#11111b style D fill:#cba6f7,color:#11111b
Todo lo que necesitas vive en @reduxjs/toolkit: createSlice, configureStore, createAsyncThunk, createEntityAdapter, createSelector reexportado de Reselect y RTK Query bajo @reduxjs/toolkit/query. Con React-Redux 9 encima tienes los hooks. Ya no instalas redux, redux-thunk ni reselect por separado: RTK los incluye y los cablea por ti.
La lección que RTK enseña trasciende a Redux y toca una distinción que Fred Brooks hizo famosa: la que separa la complejidad esencial de un problema de la accidental que arrastran sus herramientas. El modelo de Redux —un árbol de estado único, cambios descritos como acciones serializables, reducers puros sin efectos, un historial que puedes rebobinar— es complejidad esencial y valiosa: es lo que mantiene razonable una aplicación grande. Las constantes de tipo, los action creators repetidos, el switch que crece con cada caso, el spread anidado para no mutar, el cableado manual de thunk y DevTools: nada de eso era el modelo, era el peaje que pagabas por acceder a él. Durante años confundimos el peaje con el camino y concluimos que Redux era pesado, cuando lo pesado era su ceremonia. RTK no diluye el rigor —los reducers siguen siendo puros, las mutaciones de Immer se compilan a actualizaciones inmutables, cada acción sigue siendo serializable e inspeccionable—; lo que hace es cobrar el rigor sin cobrar la ceremonia. Por eso no es azúcar opcional sobre Redux sino la forma canónica de Redux, y por eso la pregunta madura de 2026 ya no es cómo escribir menos boilerplate, sino algo anterior: si tu estado es de servidor no es un problema de Redux, es un problema de cache; y solo lo que sobra —estado global de cliente que de verdad se comparte— pide un store. RTK borró el boilerplate tan bien que dejó a la vista la única decisión que siempre importó: qué clase de estado tienes de verdad.
- Toma un slice clásico tuyo —constantes, action creators, switch— y reescríbelo con
createSlice; cuenta líneas antes y después y clasifica cada línea eliminada como accidental o esencial. - Monta el store con
configureStore, abre Redux DevTools, despacha dos acciones y usa el time-travel para volver atrás: comprueba que el rigor sigue ahí sin haberlo cableado a mano. - Recorre el árbol de decisión con tres estados reales de tu app y justifica en cuáles Redux no era la respuesta correcta.
- Busca una actualización inmutable hecha a mano con spread anidado y reescríbela como mutación de Immer; explica por qué el resultado sigue siendo inmutable.
- Argumenta ante un escéptico por qué escribir Redux a mano en 2026 no es una elección conservadora sino un error, y describe la única excepción que aceptarías.