useLayoutEffect y el parpadeo
¿Cuándo vale la pena bloquear la pintura del navegador?
Al terminar vas a poder explicar
- Explicar la diferencia de timing con useEffect en términos de frames
- Identificar los pocos casos donde useLayoutEffect es la respuesta correcta
- Anticipar el costo de un setState dentro de un layout effect
useLayoutEffect tiene exactamente la misma firma que useEffect. La única diferencia es cuándo corre. Y ese cuándo cambia todo.
useLayoutEffectcorre dentro del commit, después de aplicar los cambios al DOM y antes de que el navegador pinte. Es sincrónico y bloquea la pintura.useEffectcorre después del pintado, en una tarea aparte.
Los dos efectos en la misma corrida, con el pintado en el medio
AGENDADOpaso 1 / 89Sin hooks en este paso.
Árbol de fibers
- <HostRoot>
Salida
Nada en el host todavía. React no commiteó ningún cambio.
createRoot().render()
Montaje inicial. No hay árbol previo, así que todo se va a crear desde cero.
VER EL CÓDIGO QUE SE ESTÁ TRAZANDO
1function DosEfectos() {2 const [n, setN] = useState(0);3 4 useLayoutEffect(() => {5 medirYAjustar(); // corre ANTES de pintar6 return () => limpiar();7 }, [n]);8 9 useEffect(() => {10 registrarAnalitica(n); // corre DESPUÉS de pintar11 return () => cancelar();12 }, [n]);13 14 return <button onClick={() => setN(v => v + 1)}>n = {n}</button>;15}Con el foco acá: ← → un paso · espacio reproduce · Inicio/Fin van a los extremos.
Ahí está la prueba, sin metáforas. Buscá el paso 🖌 el navegador pinta: el layout effect está arriba, el efecto pasivo está abajo. Poné la traza en «sólo hitos» si querés verlo de un vistazo.
Cuándo importa de verdad
Importa cuando tu efecto lee o modifica el layout y el resultado se ve. El caso canónico es un tooltip:
1useEffect(() => {2 const alto = ref.current.offsetHeight;3 setPosicionY(anclaY - alto); // ✗ el usuario ve el tooltip mal ubicado4}, [anclaY]); // y después saltar a su lugar5 6useLayoutEffect(() => {7 const alto = ref.current.offsetHeight;8 setPosicionY(anclaY - alto); // ✓ se corrige antes de que se pinte nada9}, [anclaY]);Con useEffect, el navegador pinta el tooltip en la posición vieja, después corre el efecto, después re-renderiza y vuelve a pintar. Son dos frames y el usuario ve el salto. Con useLayoutEffect, todo eso pasa antes del primer pintado. Un frame. Sin parpadeo.
El criterio
Usá useEffect por defecto. Pasate a useLayoutEffect sólo cuando se cumplan las dos condiciones a la vez:
- El efecto mide o ajusta algo del layout, y
- ves un parpadeo real, no uno hipotético.
Suscripciones, pedidos a la red, analítica, temporizadores: nada de eso mira el layout. Ponerlos en un layout effect no arregla nada y retrasa la pintura.
Antes de seguir · predecí
Tu efecto se suscribe a un WebSocket y actualiza una lista de mensajes. ¿useEffect o useLayoutEffect?
Lo que te llevás
Misma firma, distinto momento. useLayoutEffect corre dentro del commit y bloquea la pintura; useEffect corre después y no la bloquea. Elegí el primero sólo cuando medís layout y el parpadeo es real.
Antes de marcarla, comprobá
- ¿Podés explicar explicar la diferencia de timing con useEffect en términos de frames?
- ¿Podés explicar identificar los pocos casos donde useLayoutEffect es la respuesta correcta?
- ¿Podés explicar anticipar el costo de un setState dentro de un layout effect?