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.

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:
Aférrate a eso y React deja de sentirse mágico. Empieza a sentirse como un sistema en el que puedes confiar.
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:
SearchBox, ProfileCard, CheckoutForm)name, price, disabled)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.
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”.
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:
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.
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”.
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.
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:
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.
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:
selectedId?Si puedes responder eso en papel, el código React suele ser directo.
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.
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?”.
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:
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.
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):
OrdersPage que posea filters y selectedOrderId. OrdersTable es controlado por filters y llama a onEdit(orderId).”visibleOrders de orders y filters. No almacenes visibleOrders en el state.”EditOrderDialog que reciba order y open. Al guardar, llama a onSave(updatedOrder) y cierra.”filters a 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á.
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?
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.
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.
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.
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.
Evita guardar cualquier cosa que puedas calcular a partir de las entradas existentes. Guarda las entradas y deriva el resto durante el render.
Ejemplos:
items, filtervisibleItems = items.filter(...)Así evitas que los valores se desincronicen.
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).
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.
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.
Sucede cuando un handler “captura” un valor viejo de una renderización anterior. Soluciones comunes:
setX(prev => ...)Si un clic usa “el valor de ayer”, sospecha un cierre stale (stale closure).
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:
Así el código generado se alinea con el modelo mental de React.