wandres.dev
NIVEL DIOS: SÍNTESIS · la teoría del estado

Diseñar el estado de una app real

El plano baja al terreno. Esta lección diseña el estado de una app real de principio a fin —un tablero de proyectos colaborativo, del estilo de Linear o Trello— y demuestra que la clave no es elegir una librería, sino separar primero las clases de estado y asignar después la herramienta que cada clase pide. Recorre las siete clases —servidor, URL, formulario, efímero de UI, global de cliente, proceso con ciclo de vida y colaborativo distribuido— y ubica cada una en su punto del plano de reactividad y disciplina. La tesis operativa es que casi todo desastre de arquitectura nace de saltarse la clasificación y usar un único martillo, casi siempre un store global, para siete clavos distintos. Clasificar antes de elegir, y usar el nivel más bajo que resuelva, son las dos reglas que convierten el plano en decisiones concretas y defendibles.

⏱ 20 min

Hasta aquí el nivel ha sido teoría: dos ejes, un plano, un ecosistema ubicado. Ahora bajamos al barro y diseñamos, de principio a fin, el estado de una app real —un tablero de proyectos colaborativo, del estilo de Linear o Trello, con tareas, filtros, edición en vivo y varios usuarios a la vez—. La lección entrena la habilidad que separa al ingeniero senior del que colecciona librerías: no elige una herramienta para la app, sino que primero separa las clases de estado que la app contiene y después le asigna a cada clase la herramienta que su posición en el plano exige. Verás que una sola pantalla mezcla siete clases distintas, que cada una pide una coordenada diferente, y que casi todo desastre de arquitectura nace del mismo pecado: saltarse la clasificación y clavar siete clavos distintos con el mismo martillo, casi siempre un store global. Clasificar antes de elegir es lo que convierte el plano en decisiones.

🎯 Al terminar esta lección sabrás
  • Separar las siete clases de estado que conviven en una app real y no volver a confundirlas.
  • Asignar a cada clase la herramienta que su posición en el plano exige, con criterio y no por costumbre.
  • Diseñar de principio a fin el estado de un tablero colaborativo como caso completo.
  • Interiorizar la regla de oro: clasifica antes de elegir, y usa el nivel más bajo que resuelva.

Siete clases, no tres

El árbol de decisión que viste a mitad del track separaba tres cosas: servidor, cliente y UI. Aquella era la versión de aprendiz.

La versión de maestría refina esas tres en siete clases. No por afán de taxonomista, sino porque cada una vive en un punto distinto del plano, y confundir dos es la raíz de la mayoría de los bugs de estado.

La clave está en el orden de trabajo. Primero clasificas cada dato por su naturaleza; solo después eliges la herramienta. Invertir ese orden —elegir la librería y meterle dentro todo lo que aparezca— es exactamente cómo nace el store monstruo.

Y sobre la clase correcta actúa una segunda regla, la de la altura: usa siempre el nivel más bajo que resuelva. La disciplina de más no es gratis; se paga en ceremonia, en acoplamiento y en un árbol que se recomputa de más.

Con esas dos reglas en la mano, recorramos el tablero. Iremos clase por clase, y verás que ninguna pieza queda huérfana y ninguna cae en dos sitios a la vez.

Por qué siete y no cinco ni doce merece una respuesta. Siete es el número de naturalezas de verdad distintas que un dato puede tener: de quién es, cuánto vive, quién lo mira, si tiene fases, si se reparte entre procesos. Menos clases mezclan naturalezas; más clases parten pelos sin ganancia.

Y las siete no son igual de traicioneras. Cuatro se ubican casi solas; las otras tres engañan, porque las tres parecen pedir un store global y solo una lo pide de verdad.

Por eso las recorreremos en dos grupos: primero las cuatro fáciles, para fijar el método, y luego las tres difíciles, donde el criterio del plano de verdad decide.

Las cuatro clases fáciles de ubicar

La primera clase es el estado de servidor: las tareas, los proyectos, los usuarios. No es tuyo; es una copia de algo que vive en el backend.

Tratarlo como estado de cliente es el error más caro y más común del frontend. Es una caché, y su herramienta es TanStack Query, que gestiona frescura, revalidación e invalidación por ti, en vez de que tú reinventes esa maquinaria dentro de un store.

El síntoma de tenerlo mal es inconfundible: banderas de carga por todas partes, lógica de recarga escrita a mano y datos que se quedan viejos sin que nadie sepa cuándo refrescarlos. Todo eso desaparece en cuanto aceptas que era una caché y no una posesión.

La segunda clase es el estado de URL: qué tablero miras, qué filtros aplicas, qué tarea tienes abierta. Todo lo que deba sobrevivir a una recarga y viajar dentro de un enlace pertenece aquí.

Su herramienta son los search params o nuqs, que convierten el router en fuente de verdad compartible. El premio es que el filtro se comparte con solo copiar la URL, sin escribir una línea extra.

Ese premio tiene nombre: deep linking. Cuando el estado de navegación vive en la URL, compartir, marcar como favorito y volver atrás con el botón del navegador funcionan gratis, porque el estado y la dirección pasan a ser la misma cosa.

La tercera clase es el estado de formulario: el buffer temporal mientras editas el título o la descripción de una tarea. Vive y muere con la edición.

Su herramienta es un estado controlado local o react-hook-form. Subirlo a un store global es cargar el árbol entero con datos que a nadie más le importan y que se tiran en cuanto cancelas.

Hay un matiz de altura incluso aquí: un formulario puede vivir controlado en React sin subir a ningún store, y si el rendimiento aprieta, bajar el valor un piso más cerca del input suele bastar antes de cambiar de herramienta.

La cuarta clase es el estado efímero de UI: si un desplegable está abierto, si el ratón pasa por encima, si un modal se ve. Es puro adorno de interacción.

Su herramienta es un useState en el componente más bajo posible, y esta clase casi nunca debería salir de su componente. Cuando lo hace, suele ser señal de que alguien la confundió con estado global.

Este es, de hecho, el olfato del arquitecto experto: cuando ve estado de UI viajando lejos de su componente, sospecha antes de leer el resto, porque casi siempre hay ahí una clase mal etiquetada.

🌐

Servidor · caché

Tareas, proyectos, usuarios. No es tuyo: es copia de lo que vive en el backend. TanStack Query. Nunca en un store de cliente.

🔗

URL · compartible

Vista, filtros, tarea abierta. Lo que sobrevive a recarga y viaja en un enlace. Search params o nuqs.

📝

Formulario · buffer

El texto mientras editas. Vive y muere con la edición. Controlado local o react-hook-form.

👻

Efímero de UI · local

Desplegable abierto, hover, modal. useState en el componente más bajo. Casi nunca sale de ahí.

Las tres clases que acaban mal en un store

Las tres clases restantes son las que más se malubican, porque parecen justificar el store global que casi todo el mundo usa por defecto. Aquí el criterio del plano se gana el sueldo.

La quinta clase es el estado global de cliente genuino: el tema claro u oscuro, la sesión del usuario, si la paleta de comandos está abierta, las banderas de funcionalidad.

Es lo único que de verdad merece un store compartido, porque lo consultan partes lejanas del árbol y no encaja en ninguna clase anterior. Su herramienta es Zustand, Jotai o el contexto, según cuánta granularidad y disciplina quieras.

Ojo con esta clase: es pequeña. En una app sana cabe en unas pocas líneas, y cuando el store global empieza a engordar, casi siempre es porque se le colaron datos de las otras seis clases.

La prueba es sencilla: abre tu store global y clasifica cada campo con el árbol. Si encuentras una lista que vino del servidor o un filtro que debería estar en la URL, ya sabes por dónde empezar a adelgazarlo.

La sexta clase es el estado de proceso con ciclo de vida: el arrastrar y soltar una tarjeta entre columnas, un asistente de creación en varios pasos, la conexión en vivo que pasa por conectando, sincronizado y desconectado.

Tiene estados excluyentes y transiciones con reglas. Su herramienta es una máquina de XState, que vuelve irrepresentable el estado imposible, como el arrastre que suelta en un destino inválido o el asistente que salta un paso obligatorio.

Muchos equipos modelan estos procesos con un puñado de booleanos sueltos y sufren los estados imposibles que eso permite. La máquina no es aquí un lujo académico: es lo que impide que la tarjeta quede arrastrándose para siempre porque dos banderas se contradijeron.

La séptima clase es el estado colaborativo distribuido: el contenido de una tarjeta que dos personas editan a la vez. No basta con cachear ni con un flujo unidireccional, porque hay mutaciones concurrentes sin un orden global.

Su herramienta es un CRDTYjs, Automerge— que fusiona las ediciones sin árbitro. Es la disciplina de convergencia que reescribía el plano en la lección anterior, la capa que se añade encima del gobierno local, no en su lugar.

Fíjate en que estas tres clases difíciles no comparten ni herramienta ni coordenada: una quiere un store, otra una máquina, otra un CRDT. Aplastarlas juntas en el mismo store global, como hace tanta app, es meter tres naturalezas distintas en una sola jaula, y de ahí salen los bugs que nadie consigue reproducir.

flowchart TD
Q[Un dato de tu app] --> S{lo posee el servidor?}
S -->|si| SV[es una cache: TanStack Query]
S -->|no| U{sobrevive a recargar o viaja en un link?}
U -->|si| URL[la URL: search params o nuqs]
U -->|no| F{es un buffer de edicion temporal?}
F -->|si| FR[formulario: local o react-hook-form]
F -->|no| G{lo necesitan partes lejanas del arbol?}
G -->|no| L[local: useState lo mas abajo posible]
G -->|si| M{tiene ciclo de vida con transiciones?}
M -->|si| MAQ[maquina: XState]
M -->|no| ST[store global: Zustand Jotai o Redux]
style Q fill:#cba6f7,color:#11111b
style SV fill:#a6e3a1,color:#11111b
style URL fill:#89b4fa,color:#11111b
style MAQ fill:#f9e2af,color:#11111b

El método en marcha y la regla de la altura

Recorre el tablero entero con el árbol en la mano y verás que cada pieza cae en una sola clase. La lista de tareas es servidor, así que TanStack Query.

El filtro por etiqueta es URL, así que nuqs, y de regalo se comparte con el enlace. El título que estás editando es formulario, así que estado local que se descarta si cancelas. El menú contextual abierto es efímero, así que un useState que ni sale de la tarjeta.

El tema y la sesión son globales de cliente, así que un Zustand mínimo. El arrastre entre columnas es un proceso, así que una máquina. Y el texto coeditado es distribuido, así que un CRDT. Siete piezas, siete coordenadas, ni una forzada.

Detente en lo que acaba de pasar, porque es el corazón de la lección. No elegimos una arquitectura para el tablero: dejamos que el tablero se explicara solo, dato a dato, y las herramientas cayeron donde tenían que caer.

El orden de las operaciones es lo que importa. Primero clasificas preguntando por la naturaleza del dato —de quién es, cuánto vive, quién lo mira, si tiene fases—, y solo después eliges la herramienta que cae en esa coordenada.

Esas cuatro preguntas casi eligen la herramienta solas, porque cada respuesta es una coordenada en el plano. La elección deja de ser una opinión y pasa a ser una lectura.

Esa es la diferencia entre un diseño que puedes defender y uno que solo puedes justificar. Un diseño defendible se lee del plano; uno que solo se justifica se apoya en que a alguien le gustaba la librería.

Sobre las coordenadas actúa la regla de la altura. Si un useState local basta, no subas a un store; si un store basta, no montes una máquina; si un flujo unidireccional basta, no traigas un CRDT.

La tentación siempre empuja hacia arriba, porque la herramienta grande parece más profesional. Pero la elegancia en arquitectura de estado no es usar lo más potente, es usar lo mínimo que resuelve sin dejar deuda.

Y una advertencia para no caer en el extremo opuesto. La regla de la altura no dice que todo deba ser un useState; dice que subas cuando el dolor aparezca, no antes. Bajar de más es tan error como subir de más: un dato genuinamente global metido a la fuerza en un componente produce el prop drilling que la clase global existía para evitar.

💡
La prueba del enlace y la prueba de la recarga

Dos preguntas rápidas resuelven la mayoría de las dudas de clasificación. La prueba del enlace: si copio esta URL y se la mando a un compañero, ¿debería ver lo mismo que yo? Si la respuesta es sí —el filtro, la pestaña, la tarea abierta—, ese dato es estado de URL, no de store. La prueba de la recarga: si recargo la página, ¿este dato debe seguir ahí? Si es sí y no viene del servidor, es URL o persistencia; si es no, es efímero o de formulario. Dos preguntas de diez segundos que evitan meses de estado mal ubicado.

Arquitectura de estado es taxonomía antes que tecnología

La lección más valiosa de tu carrera con el estado no es qué librería domina en 2026, sino que la arquitectura de estado es, ante todo, un acto de clasificación, y solo después un acto de elección. El ingeniero mediocre empieza por la tecnología: le gusta Redux, o le vendieron Zustand, y mete ahí dentro las tareas del servidor, el filtro de la URL, el buffer del formulario y la bandera del modal, todo revuelto en un único objeto que crece hasta que nadie sabe por qué cambia lo que cambia. El ingeniero excelente empieza por la taxonomía: mira cada dato y le pregunta de quién es, cuánto vive, quién lo observa y si tiene ciclo de vida, y esas cuatro preguntas ya casi eligen la herramienta solas, porque cada respuesta es una coordenada en el plano. Interioriza esto y verás una app entera con otros ojos: no como un montón de datos que hay que guardar en algún sitio, sino como siete poblaciones distintas que exigen siete hábitats distintos, y meter a todas en la misma jaula es la crueldad arquitectónica de la que nacen los bugs imposibles de reproducir. La tecnología cambiará —mañana TanStack Query tendrá un rival, nuqs otro nombre, los signals otro estándar— pero las siete clases no, porque no dependen de ninguna librería: dependen de la naturaleza del dato, y la naturaleza del dato es anterior a cualquier framework que inventemos para gestionarlo. Aprende las clases y habrás aprendido algo que ninguna deprecación te puede quitar.

⚔️ Diseña el estado de una app tuya
  1. Elige una app real que conozcas y lista, sin filtrar, cada dato que su pantalla principal necesita mostrar o recordar.
  2. Pasa cada dato por el árbol de decisión y anota su clase; si alguno parece caer en dos, es que en realidad son dos datos distintos, sepáralos.
  3. Aplica la prueba del enlace y la de la recarga a los tres datos que más dudes, y reubícalos según la respuesta.
  4. Asigna a cada clase su herramienta y justifica la coordenada, con especial atención a lo que hoy tienes en un store y debería salir de él.
  5. Aplica la regla de la altura: encuentra al menos un dato sobredimensionado y bájalo al nivel más simple que lo resuelva.
  6. Dibuja el mapa de estado final de la app en una hoja, con las siete clases y su herramienta, y guárdalo como el plano que defenderías en una revisión.