Integrarlas: Redux Toolkit, Zustand y las demás
En Redux Toolkit las DevTools vienen encendidas en desarrollo y apagadas en producción sin que nadie configure nada, y ese silencio esconde decisiones que conviene conocer cuando hay varios stores, un store por petición en el servidor o acciones que no deben aparecer en el monitor. Fuera de Redux, la extensión es un protocolo abierto al que cualquier librería puede hablar: Zustand lo hace con el middleware devtools, Valtio y Jotai con sus utilidades equivalentes, y XState publica su propio inspector. Esta lección monta cada integración, nombra correctamente cada store y, sobre todo, delimita qué capacidades se heredan de verdad y cuáles se quedan en un visor bonito porque el modelo subyacente no las sostiene.
Hay una asimetría curiosa en cómo se aprende esta herramienta. Quien empieza con Redux Toolkit nunca la configura: abre la extensión y sencillamente está ahí, con todo funcionando, lo cual invita a pensar que la integración es trivial y que no hay nada que decidir. Quien llega desde Zustand o Valtio descubre lo contrario: una línea de middleware la enciende, pero lo que ve dentro no es exactamente lo mismo, y averiguar qué falta exige entender qué estaba comprando Redux con su ceremonia. Esta lección recorre las dos rutas, monta cada integración con cuidado, y termina separando lo que cualquier librería puede heredar de lo que solo se obtiene respetando el contrato completo.
- Conocer qué configura
configureStorepor defecto y cuándo hay que intervenir en esa configuración. - Nombrar y distinguir varios stores en una misma página, y manejar el caso del store por petición.
- Conectar
Zustandmediante el middlewaredevtoolsy etiquetar correctamente cada acción. - Delimitar qué capacidades se heredan de verdad y cuáles exigen el contrato completo de Redux.
En Redux Toolkit ya están encendidas
configureStore activa la integración en desarrollo y la desactiva cuando la variable de entorno indica producción. Esa decisión por defecto es correcta para la enorme mayoría de los casos y es también la razón de que casi nadie sepa que existe la opción, hasta que se topa con un escenario donde el valor por defecto no sirve. El parámetro se llama devTools y acepta tanto un booleano como un objeto de configuración.
export const store = configureStore({
reducer: raiz,
devTools: import.meta.env.DEV && {
name: 'panel-admin',
maxAge: 50,
actionsDenylist: ['raton/mover', 'lienzo/frame'],
trace: true,
},
})
Cada campo de ese objeto resuelve un problema concreto que aparece al crecer. name distingue este store de otros en el desplegable de la extensión, y se vuelve imprescindible en cuanto la página tiene más de uno. maxAge acota el historial retenido para que la extensión no se ahogue en sesiones largas. actionsDenylist excluye del monitor el ruido de alta frecuencia, que de otro modo entierra las acciones significativas. Y trace activa la captura de la pila de llamadas, cara pero decisiva cuando no sabes desde dónde se despachó algo.
Poner name cuesta cinco segundos y ahorra una tarde. En cuanto una página monta un store principal, otro de un micro-frontend y quizá un tercero de una librería incrustada, el desplegable de la extensión muestra varias entradas indistinguibles y acabas depurando el store equivocado sin darte cuenta. El síntoma es característico: despachas algo, el monitor no reacciona, y concluyes que la acción no llegó cuando en realidad estabas mirando otro sitio. Nombrar cada store convierte ese enigma en algo que se ve de un vistazo.
Hay un cuarto parámetro que casi nadie toca y que resuelve un problema real de equipos grandes: los sanitizadores de acción y de estado, que transforman lo que se envía a la extensión antes de mostrarlo. Se usan para recortar árboles enormes que hacen ilegible el panel y, sobre todo, para redactar información sensible. La última lección de este nivel los desarrolla en detalle; por ahora basta saber que la configuración de las DevTools no es solo un interruptor de encendido, sino el punto donde se decide qué se ve.
El caso que más confunde es el renderizado en servidor. Allí se crea un store por petición, y la extensión, que vive en el navegador, no interviene en absoluto durante esa fase. Basta con recordar que la configuración de las DevTools solo tiene efecto en el cliente y que un store creado en el servidor no debe intentar engancharse a nada. Con la fábrica habitual —una función que devuelve un store nuevo— esto sale bien por construcción, siempre que no se convierta el store en un singleton de módulo.
El síntoma de haberlo hecho mal es reconocible y vale la pena memorizarlo: en el cliente la extensión funciona, pero el historial arranca con acciones que el usuario no ejecutó, heredadas de una petición anterior. Es la firma de un store compartido entre peticiones, y el problema que revela es mucho más grave que la confusión del monitor, porque significa que datos de un usuario pueden llegar a la página de otro. Una vez más, la observabilidad no crea el fallo: lo hace visible antes de que alguien lo descubra por el camino difícil.
Zustand y las demás: un protocolo abierto
La extensión no es propiedad de Redux. Expone un objeto global al que cualquier librería puede hablar, y el protocolo se reduce a anunciarse al conectar y a enviar un par de acción y estado en cada cambio. Es tan simple que escribir tu propia integración para una librería que no la tenga cabe en unas veinte líneas. Zustand implementa ese diálogo en el middleware devtools, que se envuelve alrededor del creador del store.
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
export const useCarrito = create(
devtools(
(set) => ({
items: [],
anadir: (item) =>
set((s) => ({ items: [...s.items, item] }), false, 'carrito/anadir'),
vaciar: () => set({ items: [] }, false, 'carrito/vaciar'),
}),
{ name: 'carrito', enabled: import.meta.env.DEV },
),
)
El detalle que casi todo el mundo omite es el tercer argumento de set. Sin él, cada cambio aparece en el monitor con un nombre genérico y el historial se vuelve una lista de entradas idénticas, tan inútil como aquel setState de la primera lección. Con él, recuperas un registro legible. Es un recordatorio elegante de que el valor de las DevTools nunca estuvo en la extensión sino en la disciplina de nombrar intenciones, y de que Zustand, al no obligarte a nombrarlas, te deja también la responsabilidad de hacerlo.
El segundo argumento de set también tiene su historia. Es la bandera de reemplazo, que decide si el objeto devuelto sustituye al estado o se fusiona con él, y ponerla en verdadero por descuido borra todas las claves que no mencionaste. Cuando eso ocurre, el monitor lo enseña con una claridad brutal: el diff de esa acción muestra media docena de rutas desapareciendo a la vez, que es un patrón imposible de producir por accidente de ninguna otra forma. Es un ejemplo pequeño de la tesis del nivel entero, a saber, que la observabilidad no solo te deja mirar, sino que hace que ciertos errores tengan una firma visual inconfundible.
flowchart TD E[extension del navegador] --- P[protocolo compartido] P --- RTK[Redux Toolkit por defecto] P --- Z[Zustand middleware devtools] P --- V[Valtio devtools] P --- J[Jotai atoms devtools] RTK --> C1[inspeccion y diff y saltos y reproduccion] Z --> C2[inspeccion y diff y saltos parciales] V --> C3[inspeccion y diff] J --> C4[inspeccion por atomo] style C1 fill:#a6e3a1,color:#11111b style C2 fill:#f9e2af,color:#11111b style C3 fill:#fab387,color:#11111b style C4 fill:#fab387,color:#11111b
Redux Toolkit
Encendidas por defecto en desarrollo. Contrato completo: acciones nombradas, reducers puros, estado serializable.
Zustand
Middleware devtools más el tercer argumento de set. Heredas el monitor entero si te disciplinas al nombrar.
Valtio y Jotai
Utilidades propias que publican los cambios del proxy o de cada átomo. Excelente inspección, saltos limitados.
XState
Inspector propio en vez de esta extensión, porque su unidad de observación no es la acción sino la transición.
Qué se hereda de verdad y qué no
Aquí es donde la integración deja de ser una cuestión de configuración y vuelve a ser una cuestión de modelo. Las capacidades de las DevTools no son un paquete indivisible: cada una descansa sobre una propiedad concreta del sistema observado, y una librería hereda exactamente aquellas cuyas propiedades cumple.
La forma limpia de razonarlo es en escalera, de menos exigencia a más. Cada peldaño añade una restricción sobre el modelo y desbloquea una capacidad; ninguna librería obtiene un peldaño sin haber pagado los anteriores.
Inspeccionar el estado actual lo hereda cualquiera, porque solo exige poder leer un objeto. Ver el diff exige que el estado anterior siga existiendo, cosa que cumplen las librerías inmutables y que en Valtio se consigue porque el proxy sabe qué se tocó. Saltar a un instante del historial exige que el estado sea recomputable desde una lista de acciones, y ahí Zustand cumple a medias: como el middleware envía el estado completo en cada cambio, la extensión puede reinstalar un estado anterior, pero no está replegando nada, solo restaurando una fotografía. Y reproducir una sesión importada exige lo más caro de todo, que las acciones sean datos serializables suficientes para regenerar el estado, propiedad que solo se obtiene cuando el sistema fue diseñado alrededor de ellas.
La diferencia parece académica y muerde en la práctica. Cuando la extensión repliega un historial de Redux, aplica el reducer actual a las acciones antiguas, de modo que si corriges el reducer el resultado cambia y puedes verificar tu arreglo sobre la misma entrada. Cuando restaura una fotografía enviada por Zustand, simplemente reinstala un objeto guardado: corregir el código no altera nada, porque no se está recalculando. Por eso el bucle de la lección anterior —edito el reducer y vuelvo a plegar— no existe fuera del modelo de acciones, aunque el deslizador se mueva igual y la interfaz parezca obedecer del mismo modo.
Fuera del navegador: el monitor remoto
La última pieza de la integración resuelve un caso que aparece en cuanto sales de una pestaña de escritorio: el proceso que quieres observar no es el que tiene la extensión. Ocurre en React Native, donde no hay extensión de navegador; ocurre con un store que vive en un proceso de Node; y ocurre en la depuración de dispositivos, donde el código corre en un teléfono y tú miras un portátil. La solución es el monitor remoto: una aplicación de escritorio que habla el mismo protocolo por un socket, y un pequeño cliente que se conecta a ella desde donde esté tu store.
import { devToolsEnhancer } from '@redux-devtools/remote'
export const store = configureStore({
reducer: raiz,
devTools: false,
enhancers: (get) =>
get().concat(devToolsEnhancer({ realtime: true, hostname: 'localhost', port: 8000 })),
})
Fíjate en que hay que desactivar la integración estándar antes de añadir la remota, porque dos canales compitiendo por el mismo store producen historiales duplicados. Y fíjate también en el riesgo implícito: acabas de abrir un socket por el que sale el estado completo de tu aplicación. En desarrollo local es inofensivo; en cualquier compilación que pueda llegar a un dispositivo ajeno, es exactamente la clase de puerta que la lección siguiente te va a pedir que cierres, así que ese enhancer debe estar condicionado a la variable de entorno con el mismo rigor que el resto.
XState no se conecta a esta extensión y hace bien. Lo que interesa observar en una máquina de estado no es una lista plana de acciones, sino en qué estado estás, qué transiciones eran posibles y cuál se tomó; un monitor diseñado para pliegues no sabe dibujar eso. Por eso publica su propio inspector, con el diagrama de la máquina resaltando el nodo activo. La lección general es que la herramienta de observabilidad correcta la dicta la unidad de cambio del modelo, y forzar la de otro modelo produce una vista que funciona pero no explica.
La pregunta que la gente hace al comparar gestores de estado —tiene DevTools o no— está mal formulada, y esta lección existe para reformularla. Prácticamente cualquier librería puede hablarle al protocolo de la extensión y mostrar algo en su ventana; lo que ninguna puede hacer es obtener capacidades que su modelo no sostiene. Ver el estado actual es gratis porque no exige nada. Ver el diff cuesta inmutabilidad. Saltar en el tiempo de verdad, replegando en lugar de restaurar, cuesta que el estado sea una función pura de una lista de acciones. Reproducir la sesión de un usuario cuesta además que esas acciones sean datos serializables y suficientes por sí solos. Cada capacidad tiene su precio en restricciones aceptadas, y la extensión no es más que el escaparate donde se ve qué has pagado. De ahí se sigue el criterio que de verdad importa al elegir herramienta: no preguntes si tiene DevTools, pregunta qué capacidades necesitas de verdad para depurar tu dominio y qué disciplina estás dispuesto a mantener para ganarlas. Un equipo que solo necesita mirar el estado y su diff pagará de más si adopta Redux por sus DevTools, porque Zustand con el tercer argumento de set le da eso mismo por mucho menos. Un equipo que necesita reproducir sesiones de usuarios reales en un dominio auditable descubrirá que la ceremonia de Redux no era un impuesto sino el pago exacto de esa capacidad, y que ninguna librería ligera se la puede regalar sin volverse pesada por el camino. Ese es el intercambio que el nivel entero venía preparando, y es la forma madura de mirar toda esta familia de herramientas: no compras un monitor, compras las propiedades que hacen posible que un monitor te diga la verdad.
- Añade a tu
configureStoreun objetodevToolsconname,maxAgey unactionsDenylistque excluya tu acción más ruidosa. Compara la legibilidad del monitor antes y después. - Monta en una misma página dos stores y comprueba en el desplegable de la extensión que los distingues por nombre. Despacha en uno y verifica que el otro no se inmuta.
- Envuelve un store de
Zustandcon el middlewaredevtoolssin usar el tercer argumento deset. Observa el historial resultante y describe por qué es inútil. - Añade el nombre a cada acción y repite. Anota cuántas de tus acciones necesitaron que te parases a decidir qué intención representaban.
- En ese mismo store de
Zustand, salta a un estado anterior, corrige la función que lo produce y vuelve a saltar. Comprueba que la corrección no altera el resultado y explica por qué. - Repite el paso anterior en Redux Toolkit y confirma que ahí sí cambia. Escribe en dos frases qué capacidad concreta estabas comprando con la ceremonia.