Renderizado y Virtual DOM
Ya sabemos que cuando cambiamos el estado con useState, la interfaz se actualiza automáticamente. Pero ¿qué está pasando por debajo? ¿Cómo sabe React qué actualizar? En esta clase vamos a entenderlo.
¿Qué significa “renderizar”?
Renderizar es el proceso de ejecutar tu componente (tu función) para generar el JSX que se mostrará en pantalla.
function Saludo() {
return <h1>Hola</h1>
}
// Cuando React "renderiza" Saludo:
// 1. Ejecuta la función Saludo()
// 2. Obtiene el JSX: <h1>Hola</h1>
// 3. Lo convierte en elementos del DOM
Re-renderizado: Cuando cambia el estado
Cuando cambias el estado, React vuelve a ejecutar tu función componente. Veamos esto en acción con nuestro componente JobCard.
Añadiendo console.log para verlo
Actualiza tu componente JobCard para incluir un console.log:
function JobCard({ titulo, empresa, ubicacion, descripcion }) {
// ¡Añadimos esto para ver cuándo se ejecuta!
console.log('🔄 JobCard se está renderizando. Título:', titulo)
// Estado: ¿el usuario aplicó a este empleo?
const [aplicado, setAplicado] = React.useState(false)
const handleAplicar = () => {
console.log('👆 Click en aplicar')
setAplicado(true) // Cambiamos el estado a true
}
console.log('📊 Estado actual de aplicado:', aplicado)
return (
<article className="job-listing-card">
<div>
<h3>{titulo}</h3>
<small>
{empresa} | {ubicacion}
</small>
<p>{descripcion}</p>
</div>
<button
className={aplicado ? 'button-apply-job is-applied' : 'button-apply-job'}
onClick={handleAplicar}
disabled={aplicado}
>
{aplicado ? '¡Aplicado!' : 'Aplicar'}
</button>
</article>
)
}
Prueba esto
- Abre tu navegador con el archivo
- Abre la consola (F12)
- Observa los mensajes iniciales
- Haz click en “Aplicar”
- Mira qué pasa en la consola
Verás algo así:
🔄 JobCard se está renderizando. Título: Desarrollador Frontend
📊 Estado actual de aplicado: false
🔄 JobCard se está renderizando. Título: Ingeniero de Software Senior
📊 Estado actual de aplicado: false
Y cuando haces click en el primer botón:
👆 Click en aplicar
🔄 JobCard se está renderizando. Título: Desarrollador Frontend
📊 Estado actual de aplicado: true
¿Qué está pasando?
-
Renderizado inicial:
- React ejecuta cada componente
JobCard aplicadoempieza enfalse- Se muestra “Aplicar”
- React ejecuta cada componente
-
Usuario hace click:
- Se ejecuta
handleAplicar - Llama a
setAplicado(true)
- Se ejecuta
-
React detecta el cambio de estado:
- Vuelve a ejecutar la función
JobCard - Esta vez
aplicadoestrue - Genera nuevo JSX con “¡Aplicado!”
- Actualiza solo ese botón en el DOM
- Vuelve a ejecutar la función
Importante: La función componente se ejecuta completamente de nuevo cada vez que el estado cambia.
El ciclo de vida del renderizado
┌─────────────────────────────────────────┐
│ 1. Renderizado inicial │
│ • React ejecuta el componente │
│ • useState devuelve valor inicial │
│ • Se genera JSX │
│ • Se muestra en el DOM │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 2. Usuario interactúa │
│ • Hace click en el botón │
│ • Se ejecuta handleAplicar │
│ • Se llama setAplicado(true) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 3. Re-renderizado │
│ • React ejecuta el componente otra vez │
│ • useState devuelve el nuevo valor │
│ • Se genera nuevo JSX │
│ • React compara con el anterior │
│ • Actualiza solo lo que cambió │
└─────────────────────────────────────────┘
¿Por qué no se pierde el estado?
Te estarás preguntando: “Si la función se ejecuta de nuevo, ¿por qué aplicado no vuelve a false?”
Buena pregunta. La respuesta es que React guarda el estado fuera del componente.
function JobCard() {
const [aplicado, setAplicado] = React.useState(false)
// ↑ Esto NO crea un nuevo estado cada vez
// React lo mantiene guardado y te lo devuelve
}
Cómo funciona:
- Primera vez: React crea el estado con el valor inicial (
false) - Siguientes veces: React ignora el valor inicial y te devuelve el estado guardado
Es como si React tuviera una caja con tu estado:
React mantiene internamente:
{
componente: JobCard,
posición: 0,
estado: true ← Este valor se mantiene entre renders
}
Virtual DOM: La magia de React
Ahora la gran pregunta: ¿Cómo actualiza React solo lo que cambió?
El problema sin Virtual DOM
Si actualizáramos el DOM manualmente cada vez:
// Esto es ineficiente
element.innerHTML = '<button>¡Aplicado!</button>' // Recrea todo el elemento
Recrear elementos del DOM es lento y puede causar:
- ❌ Pérdida de foco en inputs
- ❌ Pérdida de posición de scroll
- ❌ Animaciones que se reinician
- ❌ Rendimiento pobre
La solución: Virtual DOM
React usa un Virtual DOM (DOM Virtual):
- Es una copia ligera del DOM real en JavaScript
- Es solo un objeto en memoria (muy rápido)
- React lo usa para comparar antes y después
¿Cómo funciona?
┌──────────────────────────────────────────────┐
│ 1. Estado actual │
│ Virtual DOM antiguo: │
│ <button>Aplicar</button> │
└──────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ 2. Cambio de estado │
│ setAplicado(true) │
└──────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ 3. Nuevo Virtual DOM │
│ React re-ejecuta el componente │
│ Genera nuevo Virtual DOM: │
│ <button disabled>¡Aplicado!</button> │
└──────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ 4. Comparación (Diffing) │
│ React compara los dos Virtual DOMs │
│ Encuentra las diferencias: │
│ • Texto cambió: "Aplicar" → "¡Aplicado!" │
│ • Se añadió atributo: disabled │
│ • Clase cambió: se añadió "is-applied" │
└──────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ 5. Actualización mínima (Reconciliation) │
│ React actualiza SOLO lo que cambió: │
│ • element.textContent = '¡Aplicado!' │
│ • element.disabled = true │
│ • element.classList.add('is-applied') │
└──────────────────────────────────────────────┘
Ejemplo visual
Imagina que tienes esto:
function JobCard({ titulo }) {
const [aplicado, setAplicado] = React.useState(false)
return (
<article>
<h3>{titulo}</h3>
<p>Descripción del empleo...</p>
<button onClick={() => setAplicado(true)}>{aplicado ? '¡Aplicado!' : 'Aplicar'}</button>
</article>
)
}
Cuando haces click:
Virtual DOM antes: Virtual DOM después:
┌─────────────────┐ ┌─────────────────┐
│ <article> │ │ <article> │
│ <h3>Título</h3>│ │ <h3>Título</h3>│ ← Sin cambios
│ <p>Desc...</p>│ │ <p>Desc...</p>│ ← Sin cambios
│ <button> │ │ <button> │
│ Aplicar │ ≠ │ ¡Aplicado! │ ← ¡CAMBIÓ!
│ </button> │ │ </button> │
│ </article> │ │ </article> │
└─────────────────┘ └─────────────────┘
React actualiza solo:
→ button.textContent = '¡Aplicado!'
No toca el <h3> ni el <p> porque no cambiaron. ¡Súper eficiente!
Ventajas del Virtual DOM
1. Rendimiento optimizado
// Si tienes 100 empleos y cambias uno:
<div>
<JobCard /> ← No se actualiza
<JobCard /> ← No se actualiza
<JobCard /> ← SOLO ESTE se actualiza
<JobCard /> ← No se actualiza ...97 más
</div>
React solo actualiza el componente que cambió, no los otros 99.
2. Batch Updates (Agrupación de actualizaciones)
React agrupa múltiples cambios de estado:
function JobCard() {
const [aplicado, setAplicado] = React.useState(false)
const [guardado, setGuardado] = React.useState(false)
const handleAplicar = () => {
setAplicado(true)
setGuardado(true)
// React agrupa estos dos cambios
// y hace UN SOLO re-render, no dos
}
}
3. Previene actualizaciones innecesarias
Si intentas poner el mismo valor:
const [count, setCount] = React.useState(0)
setCount(0) // Ya es 0
// React detecta que no cambió y NO re-renderiza
Comparación: Vanilla JS vs React
Vanilla JS
// Tienes que actualizar TODO manualmente
button.textContent = '¡Aplicado!'
button.classList.add('is-applied')
button.disabled = true
// Si te olvidas de algo, hay inconsistencias
React con Virtual DOM
// Solo cambias el estado
setAplicado(true)
// React se encarga de:
// 1. Re-ejecutar el componente
// 2. Generar nuevo Virtual DOM
// 3. Comparar con el anterior
// 4. Actualizar solo lo necesario
Cuando NO se re-renderiza
El componente NO se re-renderiza cuando:
function JobCard() {
// ❌ Variables normales NO causan re-render
let contador = 0
contador = contador + 1 // Esto NO actualiza la UI
// ✅ Solo useState causa re-render
const [aplicado, setAplicado] = React.useState(false)
setAplicado(true) // Esto SÍ actualiza la UI
}
Variables normales se resetean en cada render:
function JobCard() {
let contador = 0
console.log(contador) // Siempre 0
const handleClick = () => {
contador++ // Se incrementa...
console.log(contador) // ...pero se pierde en el próximo render
}
}
Re-renderizado por props
Un componente también se re-renderiza cuando sus props cambian:
function App() {
const [titulo, setTitulo] = React.useState('Frontend Developer')
return (
<>
<JobCard titulo={titulo} />
<button onClick={() => setTitulo('Backend Developer')}>Cambiar</button>
</>
)
}
// Cuando setTitulo cambia:
// 1. App se re-renderiza
// 2. JobCard recibe nueva prop
// 3. JobCard también se re-renderiza
Visualizando los re-renders
Puedes ver qué componentes se re-renderizan añadiendo console.log:
function App() {
console.log('🟢 App se renderiza')
const [count, setCount] = React.useState(0)
return (
<>
<Counter count={count} />
<button onClick={() => setCount(count + 1)}>+1</button>
</>
)
}
function Counter({ count }) {
console.log('🔵 Counter se renderiza con count:', count)
return <p>Count: {count}</p>
}
Al hacer click verás:
🟢 App se renderiza
🔵 Counter se renderiza con count: 1
Ambos se re-renderizan porque:
Appcambió su estadoCounterrecibió una nueva prop
Lo que hemos aprendido de los renderizados y el Virtual DOM
- 🔄 Qué es renderizar - Ejecutar el componente para generar JSX
- 🎯 Re-renderizado - Cuando cambia el estado, React vuelve a ejecutar el componente
- 🧠 Estado persistente - React guarda el estado entre renders
- 🌳 Virtual DOM - Copia ligera del DOM en JavaScript
- ⚡ Diffing - React compara Virtual DOMs para encontrar cambios
- 🎨 Reconciliation - React actualiza solo lo que cambió
- 📊 Batch Updates - React agrupa múltiples cambios
- 🔍 Optimización - React previene actualizaciones innecesarias
En la próxima clase aprenderemos a renderizar listas de elementos dinámicamente usando .map() y la importancia de la prop key.
💡 Recuerda: Cuando el estado cambia, React re-ejecuta el componente, compara el Virtual DOM antiguo con el nuevo, y actualiza solo lo que cambió en el DOM real. ¡Por eso React es tan rápido!