useLayoutEffect y medición del layout en React | Nicolás Garzón
useLayoutEffect ejecuta su trabajo después de que React modifica el DOM y antes de que el navegador pinte. Puede bloquear el pintado.
TypeScript
Copiar function Tooltip ( ) {
const ref = useRef < HTMLDivElement> ( null ) ;
const [ height, setHeight] = useState ( 0 ) ;
useLayoutEffect ( ( ) => {
setHeight ( ref. current! . getBoundingClientRect ( ) . height) ;
} , [ ] ) ;
return < div ref= { ref} style= { { top: - height } } > ... < / div> ;
} React realiza un render, commit, medición y render correctivo antes del paint visible.
Usa useLayoutEffect solo cuando el usuario vería un salto visual con useEffect.
No existe layout en el servidor. Un componente que depende de mediciones debe ejecutarse en cliente o renderizar una alternativa inicial.
Trabajo pesado dentro de useLayoutEffect retrasa el paint y puede empeorar la experiencia.
Usarlo como versión “más segura” de useEffect.
Hacer fetch o logging allí.
Medir elementos que podrían resolverse con CSS.
useLayoutEffect es una herramienta de precisión para lectura/escritura visual antes del paint, no un effect general.
Texto
Copiar render
↓
commit DOM
↓
useLayoutEffect
↓ puede actualizar state
render y commit correctivos
↓
browser paint
↓
useEffectEl objetivo es evitar que el usuario vea una posición o tamaño incorrecto durante un frame.
TypeScript
Copiar function Tooltip ( { targetRect, children } : TooltipProps) {
const ref = useRef < HTMLDivElement> ( null ) ;
const [ position, setPosition] = useState ( { top: 0 , left: 0 } ) ;
useLayoutEffect ( ( ) => {
const tooltip = ref. current;
if ( ! tooltip) return ;
const rect = tooltip. getBoundingClientRect ( ) ;
setPosition ( {
top: targetRect. top - rect. height - 8 ,
left: targetRect. left + ( targetRect. width - rect. width) / 2 ,
} ) ;
} , [ targetRect] ) ;
return (
< div
ref= { ref}
style= { { position: "fixed" , top: position. top, left: position. left } }
>
{ children}
< / div>
) ;
} React primero necesita crear el nodo para medirlo. La actualización correctiva ocurre antes del paint visible.
Alternar repetidamente lecturas y escrituras puede forzar layout varias veces:
TypeScript
Copiar const rect = node. getBoundingClientRect ( ) ;
node. style. width = ` ${ rect. width} px ` ; Agrupa lecturas y minimiza escrituras. Antes de medir, revisa si CSS puede resolver el layout mediante Grid, Flexbox, container queries o positioning.
Si el tamaño puede cambiar después del montaje, una medición única no basta. Usa ResizeObserver desde un effect o layout effect según si la corrección debe ocurrir antes del paint:
TypeScript
Copiar useLayoutEffect ( ( ) => {
const node = ref. current;
if ( ! node) return ;
const observer = new ResizeObserver ( ( [ entry] ) => {
setSize ( entry. contentRect) ;
} ) ;
observer. observe ( node) ;
return ( ) => observer. disconnect ( ) ;
} , [ ] ) ; El callback debe evitar loops de medida-escritura.
Focus iniciado por una interacción suele funcionar desde el handler:
TypeScript
Copiar inputRef. current?. focus ( ) ; Usa layout effect cuando el nodo acaba de aparecer y debe enfocarse antes de que el usuario perciba el cambio. Gestiona también restauración de foco y accesibilidad.
En servidor no hay layout. Una medición no puede determinar el HTML inicial. Estrategias:
Renderizar una posición neutral.
Ejecutar la región solo en cliente.
Usar CSS para evitar depender de medidas.
Reservar dimensiones para reducir saltos.
Un warning de useLayoutEffect en SSR indica que el resultado inicial puede depender de trabajo imposible en servidor.
Todo trabajo síncrono en useLayoutEffect retrasa el paint. Evita:
Fetching.
Parsing pesado.
Recorrer grandes árboles.
Actualizaciones incondicionales.
Medir elementos que no cambiaron.
Perfila con herramientas del navegador; el problema puede estar en style/layout y no en React.
Usa useLayoutEffect solo si puedes demostrar un frame incorrecto con useEffect. Si ambas versiones se ven iguales, prefiere useEffect.
Se ejecuta después del commit y antes del paint.
Sirve para medición y corrección visual imprescindible.
Puede bloquear la respuesta del navegador.
CSS y observers suelen ser alternativas o complementos.
No existe layout durante SSR.
¿Por qué un tooltip puede necesitar dos renders antes del primer paint?
¿Qué coste tiene un layout effect pesado?
¿Cuándo un handler basta para hacer focus?
¿Por qué CSS debería evaluarse antes de medir?
Ver respuestas
Primero debe existir el nodo para medirlo y después corregir su posición.
Bloquea el paint y retrasa la interfaz.
Cuando el nodo ya existe y el focus responde directamente a la interacción.
Porque evita JavaScript, mediciones y renders correctivos.
useEffectEvent y lógica no reactiva separa lecturas actuales de las dependencias que realmente reinician una sincronización.