useLayoutEffect и useId
useLayoutEffect
useLayoutEffect похож на useEffect, но выполняется синхронно после мутаций DOM и до отрисовки браузером.
Отличие от useEffect
useEffect— асинхронно, после отрисовкиuseLayoutEffect— синхронно, до отрисовки
Когда нужен
Когда нужно измерить DOM или синхронно изменить разметку до того, как пользователь увидит мерцание.
import { useLayoutEffect, useRef, useState } from 'react';
function Tooltip({ target }: { target: HTMLElement | null }) {
const [position, setPosition] = useState({ top: 0, left: 0 });
const tooltipRef = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
if (!target || !tooltipRef.current) return;
const rect = target.getBoundingClientRect();
setPosition({
top: rect.bottom + 8,
left: rect.left,
});
}, [target]);
return (
<div ref={tooltipRef} style={{ position: 'absolute', ...position }}>
Tooltip
</div>
);
}
Предупреждение
useLayoutEffect блокирует отрисовку. Если внутри тяжёлое вычисление — пользователь увидит задержку. Используй только когда нужен синхронный доступ к DOM.
SSR
На сервере useLayoutEffect не работает и выдаёт предупреждение. Если компонент рендерится на сервере — используй useEffect.
useId
useId генерирует уникальный ID, который стабилен между рендерами. Полезен для связки label и input.
Проблема
function Input({ label }: { label: string }) {
return (
<>
<label htmlFor="input">Заголовок</label>
<input id="input" />
</>
);
}
Если таких компонентов несколько — ID конфликтуют.
Решение
import { useId } from 'react';
function Input({ label }: { label: string }) {
const id = useId();
return (
<>
<label htmlFor={id}>{label}</label>
<input id={id} />
</>
);
}
Каждый экземпляр получит свой ID.
Особенности
- ID стабилен между рендерами
- ID уникален в пределах приложения
- Не используй для
key - Работает и на сервере, и на клиенте
const id = useId();
const nameId = `${id}-name`;
const emailId = `${id}-email`;
Итоги
useLayoutEffect— синхронно до отрисовки- Нужен для измерения DOM
- Блокирует рендер — используй осторожно
useIdгенерирует стабильные уникальные ID- Для связи
labelиinput