Modelos mentales de React: maneras de pensar al estilo de Dan Abramov
Los modelos mentales de React pueden hacer que React parezca simple: aprende las ideas clave sobre componentes, renderizado, estado y efectos, y aplícalas para construir UI rápidamente mediante chat.

Por qué React se siente difícil hasta que tienes los modelos mentales correctos
React puede resultar frustrante al principio porque ves la UI cambiar, pero no siempre puedes explicar por qué cambió. Haces clic en un botón, algo se actualiza y luego otra parte de la página te sorprende. Normalmente no es “React es raro”. Es “mi imagen de lo que React está haciendo está borrosa”.
Un modelo mental es la historia simple que te cuentas sobre cómo funciona algo. Si la historia es incorrecta, tomarás decisiones con confianza que llevan a resultados confusos. Piensa en un termostato: un mal modelo es “pongo 22°C y la habitación se vuelve 22°C al instante”. Un mejor modelo es “pongo un objetivo, y la calefacción se enciende y apaga con el tiempo para alcanzarlo”. Con la mejor historia, el comportamiento deja de sentirse aleatorio.
React funciona igual. Una vez que adoptas unas pocas ideas claras, React se vuelve predecible: puedes mirar los datos actuales y adivinar con confianza qué habrá en pantalla.
Dan Abramov ayudó a popularizar esta mentalidad de “hacerlo predecible”. El objetivo no es memorizar reglas. Es mantener un pequeño conjunto de verdades en la cabeza para poder depurar razonando, no por prueba y error.
Ten en mente estas ideas:
- La UI es resultado del state, no de un conjunto de ediciones manuales del DOM.
- Los componentes son funciones que describen cómo debe verse la UI.
- Los datos fluyen hacia abajo; los cambios suben a través de eventos.
- Los efectos están separados del renderizado y tienen un propósito específico.
Aférrate a eso y React deja de sentirse mágico. Empieza a sentirse como un sistema en el que puedes confiar.
Componentes, props y state: la imagen central
React se vuelve más fácil cuando dejas de pensar en “pantallas” y empiezas a pensar en piezas pequeñas. Un componente es una unidad reutilizable de UI. Toma entradas y devuelve una descripción de cómo debe verse la UI para esas entradas.
Ayuda tratar un componente como una descripción pura: “dados estos datos, muestra esto”. Esa descripción se puede usar en muchos lugares porque no depende de dónde viva.
Las props son las entradas. Vienen de un componente padre. Las props no son “propiedad” del componente en el sentido de que el componente las deba cambiar en silencio. Si un botón recibe label="Save", el trabajo del botón es mostrar esa etiqueta, no decidir que debería ser otra.
El state es datos que posee el componente. Es lo que el componente recuerda con el tiempo. El state cambia cuando el usuario interactúa, cuando una petición termina o cuando decides que algo debe ser distinto. A diferencia de las props, el state pertenece a ese componente (o al componente que decidas que lo posea).
Versión simple de la idea clave: la UI es una función del state. Si el state dice “loading”, muestra un spinner. Si el state dice “error”, muestra un mensaje. Si el state dice “items = 3”, renderiza tres filas. Tu trabajo es mantener la UI leyendo del state, no desplazándose hacia variables ocultas.
Una forma rápida de separar los conceptos es:
- Componente: la pieza de UI (
SearchBox,ProfileCard,CheckoutForm) - Props: lo que necesita mostrar (
name,price,disabled) - State: lo que recuerda (
isOpen,query,selectedId)
Ejemplo: un modal. El padre puede pasar title y onClose como props. El modal podría poseer isAnimating como state.
Incluso si generas UI mediante chat (por ejemplo en Koder.ai), esta separación sigue siendo la forma más rápida de mantener la cordura: decide primero qué es props y qué es state, y luego deja que la UI siga.
Renderizado y re-renderizado: qué cambia realmente
Una forma útil de mantener React en la cabeza (muy en espíritu Dan Abramov) es: el renderizado es un cálculo, no una tarea de pintado. React ejecuta tus funciones de componente para averiguar cómo debe verse la UI con las props y el state actuales. La salida es una descripción de UI, no pixeles.
Un re-render solo significa que React repite ese cálculo. No significa “se vuelve a dibujar toda la página”. React compara el nuevo resultado con el anterior y aplica el conjunto mínimo de cambios al DOM real. Muchos componentes pueden re-renderizarse mientras que solo unos pocos nodos del DOM se actualizan.
La mayoría de los re-renders ocurren por razones simples: cambió el state de un componente, cambiaron sus props, o un padre se re-renderizó y React pidió al hijo que renderizara otra vez. Ese último punto sorprende a la gente, pero normalmente está bien. Si tratas el render como “barato y aburrido”, tu app sigue siendo más fácil de razonar.
La regla que mantiene esto limpio: haz el render puro. Dados los mismos inputs (props + state), tu componente debería devolver la misma descripción de UI. Mantén las sorpresas fuera del render.
Ejemplo concreto: si generas un ID con Math.random() dentro del render, un re-render lo cambia y de repente un checkbox pierde el foco o un elemento de la lista se remonta. Crea el ID una vez (state, memo o fuera del componente) y el render se vuelve estable.
Si recuerdas una frase: un re-render significa “recalcular cómo debe ser la UI”, no “reconstruirlo todo”.
Actualizaciones de estado: mantén los cambios pequeños y predecibles
Otro modelo útil: las actualizaciones de estado son peticiones, no asignaciones instantáneas. Cuando llamas a un setter como setCount(count + 1), estás pidiéndole a React que programe un render con un nuevo valor. Si lees el state justo después, puede que aún veas el valor viejo porque React no ha renderizado todavía.
Por eso importan las actualizaciones “pequeñas y predecibles”. Prefiere describir el cambio en lugar de agarrar lo que crees que es el valor actual. Cuando el siguiente valor depende del anterior, usa la forma updater: setCount(c => c + 1). Coincide con cómo funciona React: varias actualizaciones pueden encolarse y luego aplicarse en orden.
La inmutabilidad es la otra mitad de la imagen. No cambies objetos o arrays en su lugar. Crea uno nuevo con el cambio. Así React puede ver “este valor es nuevo” y tu cerebro puede rastrear qué cambió.
Ejemplo: alternar un todo. El enfoque seguro es crear un nuevo array y un nuevo objeto todo para el elemento que cambiaste. El enfoque arriesgado es hacer todo.done = !todo.done dentro del array existente.
También mantén el state al mínimo. Una trampa común es almacenar valores que puedes calcular. Si ya tienes items y filter, no almacenes filteredItems en el state. Calcula eso durante el render. Menos variables de estado significa menos formas de que los valores se desincronicen.
Una prueba simple para qué pertenece al state:
- Guárdalo si puede cambiar con el tiempo y la UI necesita reaccionar.
- No lo guardes si puede derivarse de otro state o props.
- No guardes duplicados de la misma verdad en dos sitios.
Si construyes UI vía chat (incluido en Koder.ai), pide cambios como parches pequeños: “Añadir un booleano” o “Actualizar esta lista de forma inmutable”. Cambios pequeños y explícitos mantienen al generador y tu código React alineados.
Los efectos son para sincronizar, no para lógica básica de UI
El render describe la UI. Los efectos sincronizan con el mundo exterior. “Exterior” significa cosas que React no controla: llamadas de red, timers, APIs del navegador y a veces trabajo imperativo en el DOM.
Si algo puede calcularse a partir de props y state, normalmente no debería vivir en un efecto. Ponerlo en un efecto añade un paso extra (render, ejecutar efecto, set state, render otra vez). Ese salto extra es donde aparecen parpadeos, bucles y bugs de “¿por qué esto está obsoleto?”.
Una confusión común: tienes firstName y lastName, y guardas fullName en state usando un efecto. Pero fullName no es un efecto secundario; es dato derivado. Calculalo durante el render y siempre coincidirá.
Como hábito: deriva valores de UI durante el render (o con useMemo cuando sea realmente caro), y usa efectos para “hacer algo”, no para “averiguar algo”.
La matriz de dependencias es una lista de triggers
Trata la matriz de dependencias como: “Cuando estos valores cambien, re-sincronízate con el mundo exterior”. No es un truco de rendimiento ni un lugar para silenciar avisos.
Ejemplo: si obtienes detalles de usuario cuando cambia userId, userId pertenece a la matriz de dependencias porque debe disparar la sincronización. Si el efecto también usa token, inclúyelo, o podrías pedir con un token viejo.
Una buena comprobación: si quitar un efecto solo haría que la UI estuviera mal, probablemente no era un efecto real. Si quitarlo detendría un timer, cancelaría una suscripción o evitaría un fetch, entonces probablemente sí lo era.
Flujo de datos: una fuente de la verdad supera al estado disperso
Uno de los modelos mentales más útiles es simple: los datos bajan por el árbol, y las acciones del usuario suben.
Un padre pasa valores a los hijos. Los hijos no deberían “poseer” en secreto el mismo valor en dos sitios. Solicitan cambios llamando a una función, y el padre decide cuál es el nuevo valor.
Cuando dos partes de la UI deben estar de acuerdo, elige un lugar para almacenar el valor y pásalo hacia abajo. Esto es “elevar el estado”. Puede parecer plomería extra, pero evita un problema peor: dos estados que se desincronizan y te obligan a añadir hacks para mantenerlos alineados.
Ejemplo: un cuadro de búsqueda y una lista de resultados. Si el input guarda su propia query y la lista guarda su propia query, eventualmente verás “el input muestra X pero la lista usa Y”. La solución es mantener query en un padre, pasarlo a ambos y pasar un onChangeQuery(newValue) al input.
Elevar estado no siempre es la respuesta. Si un valor solo importa dentro de un componente, mantenlo ahí. Mantener el state cerca de donde se usa suele hacer el código más legible.
Un límite práctico:
- State local: detalles solo de UI (dropdown abierto, qué pestaña está resaltada)
- State compartido: datos que múltiples componentes deben acordar (filtros, elemento seleccionado, estado de auth)
Si dudas si elevar, busca señales: dos componentes muestran el mismo valor de formas distintas; una acción en un lugar debe actualizar algo lejano; copias props en state “por si acaso”; o añades efectos solo para mantener dos valores alineados.
Este modelo también ayuda cuando construyes mediante herramientas de chat como Koder.ai: pide un único dueño para cada pieza de state compartido y luego genera handlers que fluyan hacia arriba.
Paso a paso: diseña una feature dibujando el estado primero
Elige una feature lo suficientemente pequeña para mantener en la cabeza. Una buena es una lista buscable donde puedes hacer clic en un elemento para ver detalles en un modal.
Empieza bosquejando las partes de la UI y los eventos que pueden ocurrir. No pienses en código todavía. Piensa en lo que el usuario puede hacer y ver: hay un input de búsqueda, una lista, un resaltado de fila seleccionada y un modal. Los eventos son escribir en la búsqueda, hacer clic en un elemento, abrir el modal y cerrar el modal.
Ahora “dibuja el estado”. Escribe los pocos valores que deben almacenarse y decide quién los posee. Una regla simple funciona bien: el padre común más cercano de todos los lugares que necesitan un valor debería poseerlo.
Para esta feature, el state almacenado puede ser pequeño: query (string), selectedId (id o null) e isModalOpen (booleano). La lista lee query y renderiza elementos. El modal lee selectedId para mostrar detalles. Si tanto la lista como el modal necesitan selectedId, mantenlo en el padre, no en ambos.
Después, separa datos derivados de datos almacenados. La lista filtrada es derivada: filteredItems = items.filter(...). No la almacenes en state porque siempre puede recomputarse a partir de items y query. Guardar datos derivados es la forma en que los valores se desincronizan.
Solo entonces pregúntate: ¿necesitamos un efecto? Si los items ya están en memoria, no. Si escribir una query debe hacer fetch, sí. Si cerrar el modal debe guardar algo, sí. Los efectos son para sincronizar (fetch, guardar, suscribirse), no para cablear la UI básica.
Finalmente, prueba el flujo con algunos casos límite:
- Escribir rápido y luego borrar: ¿la lista vuelve a la normalidad?
- Hacer clic en un elemento y luego la lista cambia por la búsqueda: ¿sigue siendo válido
selectedId? - Cerrar el modal: ¿mantienes la selección o la reseteas?
- Los items se actualizan desde el servidor: ¿qué pasa si el elemento seleccionado desaparece?
Si puedes responder eso en papel, el código React suele ser directo.
Errores comunes que rompen el modelo mental
La mayoría de la confusión en React no tiene que ver con la sintaxis. Ocurre cuando tu código deja de coincidir con la historia simple que tienes en la cabeza.
Patrones de error (y el síntoma que notas)
Guardar estado derivado. Guardas fullName en state aunque solo sea firstName + lastName. Funciona hasta que un campo cambia y el otro no, y la UI muestra un valor obsoleto.
Bucles de efectos. Un efecto hace fetch, setea state, y la lista de dependencias lo hace ejecutarse otra vez. El síntoma son peticiones repetidas, UI temblorosa o un state que nunca se estabiliza.
Closures obsoletos. Un handler de clic lee un valor antiguo (como un contador o filtro desactualizado). El síntoma es “hice clic, pero usó el valor de ayer”.
State global por todas partes. Poner cada detalle de UI en un store global hace difícil saber quién posee qué. El síntoma es cambias una cosa y tres pantallas reaccionan de formas inesperadas.
Mutar objetos anidados. Actualizas un objeto o array in situ y te preguntas por qué la UI no se actualizó. El síntoma es “los datos cambiaron, pero nada se re-renderizó”.
Aquí hay un ejemplo concreto: un panel de “buscar y ordenar” para una lista. Si almacenas filteredItems en state, puede desviarse de items cuando llegan datos nuevos. En su lugar, guarda las entradas (texto de búsqueda, elección de orden) y calcula la lista filtrada durante el render.
Con los efectos, úsalos para sincronizar con el mundo exterior (fetching, suscripciones, timers). Si un efecto hace trabajo básico de UI, a menudo pertenece al render o a un handler de evento.
Al generar o editar código vía chat, estos errores aparecen más rápido porque los cambios pueden llegar en grandes bloques. Un buen hábito es enmarcar las peticiones en términos de propiedad: “¿Cuál es la fuente de la verdad para este valor?” y “¿Podemos calcular esto en vez de almacenarlo?”.
Lista rápida cuando el estado empieza a sentirse desordenado
Cuando tu UI empieza a ser impredecible, raramente es “demasiado React”. Normalmente es demasiado state, en los lugares equivocados, haciendo trabajos que no debería hacer.
Antes de añadir otro useState, pausa y pregúntate:
- ¿Cuál es el conjunto mínimo de valores que describe completamente lo que el usuario ve ahora mismo?
- ¿Dónde debería vivir cada valor para que las partes que lo necesiten puedan leerlo y las que no lo necesitan se mantengan tranquilas?
- ¿Es este valor realmente información nueva, o puedes calcularlo a partir de props, parámetros de URL u otro state?
- ¿Estás usando un efecto para calcular UI, o solo para sincronizar con algo fuera de React (red, timers, APIs del navegador)?
- Si un valor cambia, ¿qué componentes deberían re-renderizar porque lo leen, y cuáles no?
Ejemplo pequeño: cuadro de búsqueda, dropdown de filtro, lista. Si guardas query y filteredItems en state, ahora tienes dos fuentes de la verdad. En su lugar, mantén query y filter en state, y deriva filteredItems durante el render a partir de la lista completa.
Esto importa cuando construyes rápido mediante herramientas de chat también. La velocidad es genial, pero sigue preguntando: “¿Añadimos state, o añadimos por accidente un valor derivado?” Si es derivado, borra ese state y calcúlalo.
Ejemplo: iterar en una UI rápido sin perder predictibilidad
Un equipo pequeño está construyendo una UI de administración: una tabla de pedidos, algunos filtros y un diálogo para editar un pedido. La primera petición es vaga: “Añade filtros y un popup de edición”. Suena simple, pero suele convertirse en estado aleatorio esparcido por todas partes.
Hazlo concreto traduciendo la petición a estado y eventos. En lugar de “filtros”, nombra el state: query, status, dateRange. En lugar de “popup de edición”, nombra el evento: “usuario hace clic en Edit en una fila”. Luego decide quién posee cada pieza de state (página, tabla o diálogo) y qué puede derivarse (como una lista filtrada).
Ejemplos de prompts que mantienen el modelo intacto (también funcionan bien en constructores por chat como Koder.ai):
- “Crea un
OrdersPageque poseafiltersyselectedOrderId.OrdersTablees controlado porfiltersy llama aonEdit(orderId).” - “Deriva
visibleOrdersdeordersyfilters. No almacenesvisibleOrdersen el state.” - “Añade un
EditOrderDialogque recibaorderyopen. Al guardar, llama aonSave(updatedOrder)y cierra.” - “Si los datos se obtienen por fetch, mantenlos en un solo lugar. Usa un efecto solo para sincronizar
filtersa la URL, no para calcular las filas filtradas.”
Después de que la UI se genere o actualice, revisa los cambios con una comprobación rápida: cada valor de state tiene un dueño, los valores derivados no están almacenados, los efectos solo sincronizan con el exterior (URL, red, almacenamiento) y los eventos fluyen hacia abajo como props y hacia arriba como callbacks.
Cuando el state es predecible, iterar se siente seguro. Puedes cambiar el layout de la tabla, añadir un filtro nuevo o retocar los campos del diálogo sin adivinar qué state oculto se romperá.
Próximos pasos: construye más rápido con chat, pero conserva el modelo React
La velocidad solo sirve si la app sigue siendo fácil de razonar. La protección más simple es tratar estos modelos mentales como una lista de verificación que aplicas antes de escribir (o generar) UI.
Empieza cada feature de la misma manera: escribe el state que necesitas, los eventos que pueden cambiarlo y quién lo posee. Si no puedes decir “Este componente posee este state, y estos eventos lo actualizan”, probablemente acabarás con state disperso y re-renders sorprendentes.
Si construyes mediante chat, empieza en modo planificación. Describe los componentes, la forma del estado y las transiciones en lenguaje claro antes de pedir código. Por ejemplo: “Un panel de filtros actualiza el state query; la lista de resultados se deriva de query; seleccionar un elemento establece selectedId; cerrar lo limpia.” Cuando eso se lea claro, generar la UI se vuelve un paso mecánico.
Si usas Koder.ai (koder.ai) para generar código React, vale la pena hacer una pasada de saneamiento rápida antes de continuar: un dueño claro por cada valor de state, UI derivada del state, efectos solo para sincronizar y ninguna fuente duplicada de la verdad.
Luego itera en pasos pequeños. Si quieres cambiar la estructura del state (por ejemplo, de varios booleanos a un único campo status), haz un snapshot primero, experimenta y revierte si el modelo mental empeora. Y cuando necesites una revisión más profunda o una entrega, exportar el código fuente facilita responder la pregunta real: ¿la forma del state sigue contando la historia de la UI?
Preguntas frecuentes
¿Cuál es el modelo mental más simple para que React sea predecible?
UI = f(state, props) es un buen modelo inicial. Tus componentes no “editan el DOM”; describen lo que debería verse en pantalla para los datos actuales. Si la pantalla está mal, inspecciona el state/props que la produjeron, no el DOM.
¿Cómo decido si algo debe ser props o state?
Props son entradas desde un padre; tu componente debe tratarlas como de solo lectura. State es memoria que pertenece a un componente (o al componente que elijas como dueño). Si un valor debe compartirse, elévalo al padre y pásalo como props.
Cuando React “re-renderiza”, ¿está reconstruyendo toda la página?
Un re-render significa que React vuelve a ejecutar la función del componente para calcular la próxima descripción de UI. No significa automáticamente que toda la página se repinte. React después actualiza el DOM real con los cambios mínimos necesarios.
¿Por qué mi state parece “un paso atrás” después de llamar a setState?
Porque las actualizaciones de estado se programan, no son asignaciones inmediatas. Si el siguiente valor depende del anterior, usa la forma con updater para no basarte en un valor posiblemente obsoleto:
setCount(c => c + 1)
Esto funciona incluso si hay varias actualizaciones en cola.
¿Cómo evito errores por “state derivado”?
Evita guardar cualquier cosa que puedas calcular a partir de las entradas existentes. Guarda las entradas y deriva el resto durante el render.
Ejemplos:
- Guarda:
items,filter - Deriva:
visibleItems = items.filter(...)
Así evitas que los valores se desincronicen.
¿Cuándo debo usar useEffect y cuándo evitarlo?
Usa efectos para sincronizar con lo que React no controla: fetch, suscripciones, timers, APIs del navegador o trabajo imperativo con el DOM.
No uses un efecto solo para calcular valores de UI a partir del state: calcúlalos durante el render (o con useMemo si es caro).
¿Cuál es la forma correcta de pensar sobre la matriz de dependencias de useEffect?
Trata la matriz de dependencias como una lista de triggers: “cuando estos valores cambian, re-sincroniza”. Incluye cada valor reactivo que el efecto lee.
Si dejas algo fuera, arriesgas datos obsoletos (por ejemplo, un userId o token viejo). Si agregas cosas de más, puedes crear bucles — a menudo señal de que el efecto está haciendo trabajo que debería estar en un handler o en el render.
¿Cómo sé cuándo elevar el state?
Si dos partes de la UI deben coincidir siempre, pon el state en el padre común más cercano, pásalo hacia abajo y pasa callbacks hacia arriba.
Prueba rápida: si duplicas el mismo valor en dos componentes y escribes efectos para “mantenerlos sincronizados”, ese state probablemente necesita un único dueño.
¿Por qué a veces mis manejadores de eventos usan valores obsoletos?
Sucede cuando un handler “captura” un valor viejo de una renderización anterior. Soluciones comunes:
- Usa setters con updater:
setX(prev => ...) - Pon valores cambiantes en state/refs, no en variables simples
- Asegúrate de que los efectos enumeren los valores que leen
Si un clic usa “el valor de ayer”, sospecha un cierre stale (stale closure).
¿Cómo puedo construir UI React rápidamente vía chat sin crear estado desordenado?
Empieza con un plan pequeño: componentes, dueños del estado y eventos. Luego genera código en pequeños parches (añadir un campo de estado, un handler, derivar un valor) en lugar de grandes reescrituras.
Si usas un generador por chat como Koder.ai, pide:
- un dueño claro por cada valor de estado
- valores derivados calculados en render
- efectos solo para sincronizar (fetch/URL/storage), no para la lógica básica de UI
Así el código generado se alinea con el modelo mental de React.