wandres.dev
CUÁNDO REDUX · y las alternativas

El árbol de decisión: servidor, cliente, UI

El cierre del nivel convierte todo lo anterior en un procedimiento: clasificar cada pieza de estado por su dueño y su forma, y asignarle la herramienta mínima que resuelva esa clase. Esta lección fija las tres preguntas que clasifican cualquier estado —quién posee la verdad, si es compartido o local, si es derivado o interdependiente— y las recorre en un árbol de decisión que deja cada pieza en su hoja con su herramienta idiomática de 2026: TanStack Query para el servidor, la URL para lo navegable, useState para lo local, Zustand para el cliente global plano, Jotai para el grafo de derivación, y Redux Toolkit solo cuando la escala del equipo, la estrictez del dominio o el time-travel lo justifican. Cierra con el principio de la herramienta mínima y enlaza este árbol local con el árbol de arquitectura unificado del final del track.

⏱ 18 min

Este nivel abrió delimitando el nicho de Redux y siguió repartiendo el resto del estado entre Zustand, Jotai y TanStack Query. La lección de cierre reúne todo en una sola herramienta, deliberadamente modesta: un árbol de decisión. La tesis es que, una vez sabes clasificar una pieza de estado, elegir su herramienta deja de ser una cuestión de gusto o de moda y se vuelve casi mecánico. El árbol no piensa por ti —las respuestas exigen honestidad, y esa es la parte difícil— pero estructura la decisión de modo que el sesgo tenga menos por dónde colarse. Recórrelo con sinceridad y cada pieza de tu app cae en la hoja de su herramienta mínima, que es siempre la correcta.

🎯 Al terminar esta lección sabrás
  • Clasificar cualquier pieza de estado con tres preguntas: quién la posee, si es compartida y si es derivada.
  • Recorrer el árbol que asigna a cada clase su herramienta idiomática de 2026.
  • Aplicar el principio de la herramienta mínima frente al reflejo de unificar el estado.
  • Conectar este árbol local de librerías con el árbol de arquitectura unificado del cierre del track.

Las tres preguntas que clasifican cualquier estado

Antes de nombrar ninguna herramienta hay que clasificar, y toda la clasificación se reduce a tres preguntas encadenadas. La primera y más importante: ¿quién posee la verdad de este dato? Si vive en el servidor y tú solo tienes una copia, es una cache y su clase queda decidida ahí mismo, sin más preguntas. Si lo posee el cliente —nadie lo cambia a tus espaldas— sigue en pie la segunda pregunta.

La segunda: ¿es compartido o local? Si solo un componente lo usa, es estado local y no necesita ningún store; si muchos lo leen, es global y merece un lugar compartido. Y entre ambos hay un caso especial que conviene apartar pronto: si el dato debería sobrevivir a un recargado o poder compartirse por enlace —un filtro, la pestaña activa, una búsqueda—, su sitio no es un store sino la URL. La tercera pregunta afina el estado compartido: ¿es un valor plano o un grafo de derivaciones interdependientes? Un puñado de banderas independientes pide un store plano; una red de valores que se computan unos de otros pide átomos.

Hay una cuarta pregunta implícita que conviene explicitar para los casos que no son de datos sino de flujo: ¿este estado modela modos excluyentes con reglas de transición? Si la respuesta es sí —un proceso con estados y aristas prohibidas— ninguna de las herramientas de este nivel es la respuesta primaria, sino una máquina de estado del bloque de Niveles 9 a 16, quizá alojada dentro de una de ellas. El árbol de esta lección asume que ya descartaste esa forma; si no lo has hecho, la checklist del Nivel 16 es el paso previo, no este árbol.

💡
La primera pregunta hace casi todo el trabajo

De las tres, la del dueño de la verdad es la que más decide, y suele bastar para colocar la mitad del estado de una app. En cuanto identificas que un dato vino de una petición HTTP, su clase está fijada —es una cache— y ninguna consideración posterior la cambia: da igual que muchos componentes lo lean o que parezca global, no va al store, va a TanStack Query. Empieza siempre por ahí. Separar limpiamente lo que el cliente posee de lo que solo refleja es el corte que elimina más bugs de sincronización, y hacerlo primero evita que el resto del árbol se contamine.

El árbol

Las tres preguntas se encadenan en un árbol que se recorre de arriba abajo y deja cada pieza en la hoja de su herramienta mínima. Cada bifurcación es una de las preguntas anteriores; cada hoja, una herramienta idiomática de 2026.

flowchart TD
A[una pieza de estado] --> B{quien posee la verdad?}
B -->|vive en el servidor| C[TanStack Query o SWR o RSC]
B -->|lo posee el cliente| D{deberia vivir en la URL?}
D -->|navegable o compartible| E[router o nuqs]
D -->|no| F{lo usa mas de un componente?}
F -->|solo uno| G[useState o useReducer]
F -->|varios| H{es plano o un grafo derivado?}
H -->|valores planos| I[Zustand]
H -->|grafo interdependiente| J[Jotai]
I --> K{equipo grande o dominio estricto o time-travel?}
K -->|si| L[Redux Toolkit y RTK Query]
K -->|no| I
style C fill:#f38ba8,color:#11111b
style E fill:#fab387,color:#11111b
style G fill:#a6e3a1,color:#11111b
style I fill:#94e2d5,color:#11111b
style J fill:#89b4fa,color:#11111b
style L fill:#cba6f7,color:#11111b

Léelo hoja por hoja. Verdad remota: TanStack Query, o SWR para lecturas simples, o directamente los React Server Components cuando el dato ni siquiera necesita cruzar al cliente. Dato navegable o compartible por enlace: la URL, con nuqs o el propio router, no un store. Dato que solo un componente usa: useState, o useReducer si tiene forma de máquina —la FSM a mano del Nivel 16—. Estado global plano: Zustand. Grafo de valores interdependientes: Jotai. Y solo desde la hoja de Zustand cuelga una última bifurcación: si sobre ese estado global pesan la escala de un equipo grande, la estrictez de un dominio auditable o la necesidad real de viaje en el tiempo, subes a Redux Toolkit con RTK Query; si no, te quedas en Zustand.

🌐

Servidor → TanStack Query

Todo lo que vino de una petición HTTP. La primera pregunta lo aparta antes que nada: es cache, no estado, y no vuelve a discutirse.

🔗

URL → nuqs o router

Filtros, pestañas, búsquedas: estado compartible por enlace y restaurable al recargar. La URL es un store que el navegador te regala.

🟢

Global plano → Zustand

Verdad del cliente que muchos leen y que no forma un grafo. Un store diminuto con selectores; sube a Redux solo si la escala lo exige.

🔵

Grafo derivado → Jotai

Valores interdependientes o colecciones dinámicas. Átomos que se recomputan solos; el estado real son las fuentes, no las derivaciones.

Un ejemplo recorrido fija el método. Toma un panel de administración con cinco piezas: la tabla de usuarios, el término de búsqueda, si la barra lateral está plegada, el borrador del formulario de edición y el total de usuarios activos de una tarjeta. La tabla vino del servidor: cache, TanStack Query. El término de búsqueda es navegable y compartible: URL, nuqs. La barra lateral plegada la posee el cliente, la leen varios componentes y es plana: Zustand. El borrador, con campos que se validan entre sí, es un grafo local: Jotai. Y el total de activos no es estado en absoluto: es una derivación de la tabla, así que no se guarda en ningún sitio, se computa de lo que ya tiene TanStack Query. Cinco piezas, cinco hojas, y ni una sola decisión tomada por gusto.

Fíjate en que la última pieza —el total de activos— es la trampa más común: parece estado y no lo es. Antes de asignar herramienta a cualquier valor, pregúntate si es fuente o consecuencia; si se deduce de otro dato, no entra en el árbol, porque no hay que gobernarlo, hay que calcularlo. El árbol clasifica estado, y media victoria es reconocer qué de lo que llamas estado en realidad no lo era.

El principio de la herramienta mínima

El árbol tiene una propiedad deliberada: siempre te deja en la herramienta más pequeña que resuelve tu clase de estado, y esa modestia es el principio rector de todo el nivel. La razón es una asimetría de costes que ya viste con las máquinas de estado. Equivocarse por defecto —usar Zustand donde acabará haciendo falta Redux— se corrige con una migración acotada el día que el dolor aparezca. Equivocarse por exceso —montar Redux para cuatro banderas— se paga cada día, en ceremonia y curva, hasta que alguien se atreve a quitarlo. Ante la duda, el error barato es quedarse corto, así que el árbol está sesgado hacia abajo: solo subes de herramienta cuando una necesidad concreta te empuja, nunca por si acaso.

Ese carácter mecánico se puede escribir como código, y hacerlo revela hasta qué punto la decisión, una vez tienes las respuestas, es contar y elegir:

type Clase = 'servidor' | 'url' | 'local' | 'global' | 'derivado'

function herramienta(clase: Clase, equipoGrande: boolean): string {
  switch (clase) {
    case 'servidor': return 'TanStack Query'
    case 'url':      return 'router o nuqs'
    case 'local':    return 'useState'
    case 'derivado': return 'Jotai'
    case 'global':   return equipoGrande ? 'Redux Toolkit' : 'Zustand'
  }
}

La función es honesta hasta cierto punto y deshonesta a partir de ahí. Honesta porque muestra que, con la clase ya decidida, elegir es casi automático. Deshonesta porque el parámetro clase esconde el juicio difícil —clasificar— que ningún tipo puede tomar por ti, y porque equipoGrande comprime en un booleano los cuatro argumentos matizados del Nivel 23.1. El árbol como código es una caricatura útil: dibuja la forma de la decisión, pero las respuestas siguen exigiendo la honestidad de las tres preguntas.

Un corolario refuerza el sesgo hacia abajo: subir de herramienta suele ser barato y bajar suele ser caro. Pasar de Zustand a Redux Toolkit el día que el equipo crece es cambiar cómo se despachan unos cambios, no rediseñar el estado. Pero quitar Redux de una app que nunca lo necesitó exige desmontar acciones, reducers y selectores que se enredaron por todas partes durante meses. Como añadir disciplina cuesta menos que retirarla, empezar por la herramienta mínima y subir solo bajo demanda es la estrategia de menor arrepentimiento.

⚠️
No unifiques lo que el árbol separa

La tentación recurrente es colapsar el árbol en una sola herramienta por comodidad —meter servidor, cliente y derivado en un mismo store para no gestionar tres cosas—. Es el reflejo del monolito, y reaparece incluso en quien ya abandonó Redux, ahora con Zustand como cajón único. Resístelo. La unificación no ordena el estado: obliga a cada clase a vivir con las herramientas de otra, y reintroduce justo los bugs de sincronización que la división elimina. Tres herramientas bien delimitadas son más simples de operar que una forzada a fingir que sirve para todo, porque la simplicidad real no está en el número de librerías, sino en que cada dato tenga un único dueño evidente.

Conexión con el árbol de arquitectura del track

Este árbol es local: decide entre librerías de estado para una app de componentes. Al final del track lo reencontrarás absorbido en un árbol mayor, el de elegir arquitectura, que opera un nivel por encima. Aquel no pregunta qué librería, sino qué disciplina de mutación: si tu problema pide la libertad de un signal, la red de seguridad de una máquina de estado del bloque de Niveles 9 a 16, el flujo unidireccional estricto de Elm o TCA, o la simple separación de clases que este nivel enseñó. Las herramientas de hoy —TanStack Query, Zustand, Jotai, Redux Toolkit— son las hojas más concretas de aquel árbol más abstracto.

La relación entre ambos árboles reproduce los dos ejes de la ontología con que empezó el track. El árbol de arquitectura decide cuánta disciplina de mutación impones; este árbol de librerías decide, dentro de la disciplina más ligera —la separación de clases—, qué mecanismo de reactividad concreto usa cada pieza. Dominar este árbol pequeño, con sus tres preguntas y sus seis hojas, es lo que hará legible el mapa completo cuando llegues a él: no estarás eligiendo entre nombres de moda, sino ubicando cada pieza de estado en un plano que ya entiendes.

📝
La clasificación se re-evalúa, no se firma una vez

Igual que la decisión sobre las máquinas de estado, esta no es permanente. Un dato que hoy es local puede volverse compartido cuando una segunda pantalla lo necesite; un Zustand que basta hoy puede pedir la disciplina de Redux si el equipo se cuadruplica; un valor que guardabas puede descubrirse derivable y desaparecer del estado real. Vuelve a pasar las tres preguntas cuando el problema cambie de forma, no solo cuando nazca. El coste de quedarse un peldaño corto y el de pasarse se corrigen igual: reclasificando la pieza y moviéndola a la hoja que ahora le toca.

No eliges una librería de estado: clasificas el estado y la librería se deduce

La forma equivocada de leer este nivel entero es como un catálogo para decidir qué gestor de estado adoptar, con Redux, Zustand, Jotai y TanStack Query compitiendo por tu favor. La forma correcta es entender que nunca eliges una librería en abstracto: clasificas cada pieza de estado por su naturaleza —de quién es la verdad, qué ámbito tiene, qué forma tiene— y la herramienta se deduce de esa clasificación casi sin margen. Por eso el árbol no empieza preguntando qué librería te gusta, sino quién posee la verdad del dato, porque esa pregunta es sobre el problema, no sobre la solución, y todo el error histórico de este dominio consistió en invertir ese orden. Quien elige la librería primero —Redux porque es serio, Zustand porque es moderno, lo que sea porque es lo que conoce— acaba forzando todo el estado a caber en la forma de esa herramienta, y paga el desajuste en cada dato que vive donde no le corresponde: el servidor fingiendo ser estado propio, el total guardado en vez de derivado, el filtro atrapado en un store en vez de en la URL. Quien clasifica primero descubre que la mayoría de las decisiones ya estaban tomadas por la naturaleza del dato, y que su libertad real se reduce a un puñado de bifurcaciones honestas. El árbol de este nivel y el de arquitectura del cierre del track son, en el fondo, el mismo gesto a dos escalas: mirar el problema antes que la herramienta, dejar que la forma del estado dicte su gobierno y no al revés. Esa inversión —del catálogo de librerías al análisis del estado— no es una técnica más entre otras; es la única destreza que separa a quien colecciona herramientas de quien diseña sistemas, y es lo único que este nivel, y en buena medida todo el track, quería enseñarte.

⚔️ Recorre el árbol con el estado real de tu app
  1. Haz un inventario de todas las piezas de estado de un módulo real. No agrupes ni decidas herramientas todavía: solo enuméralas.
  2. Para cada pieza, responde la primera pregunta —quién posee la verdad— y aparta de inmediato todo lo que resulte ser cache de servidor.
  3. Recorre las otras dos preguntas con lo que quede y coloca cada pieza en una hoja del árbol. Anota la herramienta idiomática de cada hoja.
  4. Compara el resultado con tu implementación actual. Cada divergencia —un dato de servidor en el store, un total guardado, un filtro fuera de la URL— es candidata a corregir.
  5. Para cada pieza que hoy vive en una herramienta más pesada que su hoja, pregúntate si hay un dolor concreto que lo justifique o solo inercia. Baja de peldaño lo que no lo tenga.
  6. Escribe tu propia versión de la función herramienta con las clases reales de tu dominio y úsala como checklist. Recuerda que el juicio difícil no es la función, sino clasificar bien su entrada.