React
Render, commit y actualización del DOM
Diferencia las fases de trigger, render y commit, y explica cuándo React calcula la interfaz, actualiza el DOM y permite ejecutar efectos.
- Última actualización
- Actualizada
- Nivel
- Fundamentos
React
Diferencia las fases de trigger, render y commit, y explica cuándo React calcula la interfaz, actualiza el DOM y permite ejecutar efectos.
Una actualización sigue un flujo conceptual:
se solicita una actualización
↓
React ejecuta componentes
↓
calcula el siguiente árbol
↓
compara tipo, posición y key
↓
aplica cambios necesarios
↓
el navegador calcula y pintaSeparar estas fases explica por qué el código de render debe ser puro y por qué algunos efectos ocurren después de que la interfaz ya fue aplicada.
Durante render React llama componentes para calcular elementos:
function OrderSummary({ order }: { order: Order }) {
const total = order.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0,
);
return (
<section>
<h2>Resumen</h2>
<p>Total: {total}</p>
</section>
);
}React no inserta cada línea directamente en el DOM mientras ejecuta la función. Primero obtiene una descripción del resultado.
props + state + context
↓
component render
↓
React element treeEn client rendering:
const root = createRoot(document.getElementById("root")!);
root.render(<App />);React calcula el árbol inicial y después crea los nodos necesarios en el contenedor.
En hydration, el navegador ya tiene HTML generado por servidor y React intenta conectarlo con el árbol cliente mediante hydrateRoot.
Un componente puede volver a ejecutarse por:
No se limita a “cambiaron sus props”. El padre puede renderizar y ejecutar al hijo aunque las props terminen siendo iguales.
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount((value) => value + 1)}>{count}</button>
<Child name="Nicolás" />
</>
);
}Al actualizar count, Parent vuelve a ejecutarse. Por defecto Child también participa en el nuevo render.
Eso no significa que su DOM cambie. Si el resultado continúa igual, React conserva los nodos existentes.
Cada render recibe una fotografía de state:
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count);
}
return <button onClick={handleClick}>{count}</button>;
}El handler pertenece al render que observaba un valor concreto. setCount solicita otra actualización; no modifica la variable count capturada por ese render.
React puede agrupar varias actualizaciones antes de renderizar:
setCount((current) => current + 1);
setCount((current) => current + 1);
setCount((current) => current + 1);Los updater functions se procesan en orden y producen un incremento de tres.
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);Las tres expresiones utilizan el mismo snapshot, por lo que normalmente solicitan el mismo valor siguiente.
La explicación útil no es simplemente “state es asíncrono”, sino que cada render tiene un snapshot y React procesa una cola de actualizaciones.
React puede ejecutar render más de una vez, pausarlo o descartar su resultado. Por eso render no debe producir efectos externos:
function Checkout() {
localStorage.setItem("visitedCheckout", "true"); // Incorrect during render
return <h1>Checkout</h1>;
}La escritura pertenece a un evento o effect según la intención.
Un render puro sí puede:
Después de calcular el siguiente árbol, React compara identidades y estructuras relevantes.
previous tree + next tree
↓
type + position + key
↓
preserve, update, mount or removeNo es una comparación profunda genérica de cada objeto JavaScript. React utiliza reglas del árbol y del renderer para decidir qué trabajo aplicar.
{isEditing ? <EditForm /> : <Preview />}Los tipos cambian en la misma posición. React desmonta uno y monta el otro.
<UserForm key={userId} userId={userId} />Cambiar userId cambia la key y expresa una identidad nueva; el state interno se reinicia.
La posición se analiza dentro del árbol que devuelve el padre, no según la línea física del archivo.
En commit React aplica operaciones al host environment:
render result
↓
commit mutations
↓
DOM updatedCommit debe ser consistente. React no deja visible medio árbol de una actualización completa.
Después del commit, el navegador todavía puede necesitar:
React commit
↓
style and layout
↓
paint and compositeuseEffect suele ejecutarse después del commit y normalmente después de que el navegador tuvo oportunidad de pintar. useLayoutEffect se ejecuta antes del paint y puede bloquearlo.
function Status({ online }: { online: boolean }) {
return <p>{online ? "En línea" : "Desconectado"}</p>;
}Cuando online cambia, React puede conservar el mismo nodo <p> y actualizar únicamente su contenido de texto.
Cuando un render produce exactamente el mismo resultado relevante, puede no existir ninguna mutación DOM.
Render no significa mount.
{showPanel && <Panel />}Al pasar de false a true, Panel monta. Mientras permanezca en la misma identidad, sus actualizaciones son renders, no montajes nuevos.
React asigna referencias al aplicar el árbol:
const inputRef = useRef<HTMLInputElement>(null);
return <input ref={inputRef} />;Durante el primer render inputRef.current todavía no contiene el nodo nuevo. Después del commit puede utilizarse desde handlers o effects.
No leas layout durante render para decidir qué devolver.
render
↓
commit
↓
layout effects
↓
paint
↓
normal effectsEste diagrama es conceptual; el navegador y scheduler pueden variar detalles. La frontera importante es que effects no forman parte del cálculo puro de render.
En desarrollo Strict Mode ejecuta comprobaciones que pueden incluir:
No reproduce estas comprobaciones adicionales de la misma forma en producción.
Si el componente rompe al renderizar dos veces, el problema suele ser una mutación o cleanup incompleto, no Strict Mode.
React puede preparar una actualización no urgente de forma interruptible:
start rendering
↓
higher-priority update arrives
↓
pause or discard work
↓
continue with relevant treeEsto no significa que tus componentes se ejecuten en paralelo en múltiples hilos. Significa que React puede planificar y descartar trabajo antes del commit.
Por eso los efectos durante render serían peligrosos: podrían ejecutarse aunque ese árbol nunca llegue a mostrarse.
Cuando un descendiente suspende, React puede abandonar el intento actual, mostrar una boundary o reintentar cuando el recurso esté listo.
El componente no debe depender de ejecutarse exactamente una vez.
memo puede evitar ejecutar un componente cuando sus props son iguales según la comparación configurada:
const ProductRow = memo(ProductRowComponent);No impide actualizaciones por state propio o Context. Tampoco garantiza que la optimización sea beneficiosa.
Renderizar de nuevo no es un error por sí mismo.
React administra los nodos que renderiza. Modificarlos manualmente puede entrar en conflicto:
inputRef.current?.remove(); // React still believes the node belongs to the treeUsa refs para operaciones compatibles como focus, scroll, medición o integración controlada. Deja que state determine la estructura:
setVisible(false);function TaskList({ tasks }: { tasks: Task[] }) {
return (
<ul>
{tasks.map((task) => (
<TaskItem key={task.id} task={task} />
))}
</ul>
);
}Si cambia el orden, las keys permiten asociar cada TaskItem con la misma entidad. Sin una identidad estable, state de inputs o componentes puede quedar asociado al elemento incorrecto.
Pregunta en orden:
React DevTools Profiler ayuda a observar commits. Un console.log dentro del componente solo confirma ejecución, no mutación DOM.
console.log dentro del componente?Identidad, reconciliación y tipos de elementos profundiza en cómo React decide qué instancia preservar, actualizar o reiniciar.