Откуда useLayoutEffect берет информацию о DOM-элементе до рендера в React
Что проверяют
Заметите ли вы, что в вопросе спрятана неверная посылка. useLayoutEffect ничего не берёт до рендера: к моменту его вызова DOM уже построен и обновлён. Интервьюер смотрит, различаете ли вы фазу рендера, фазу коммита и отрисовку кадра браузером.
Как отвечать
Аккуратно поправьте формулировку и разложите порядок. Рендером в React называется вызов функции компонента: там DOM ещё не тронут, а ref.current пуст. Дальше идёт коммит: React применяет изменения к дереву, проставляет рефы и синхронно вызывает layout-эффекты. Браузер в этот момент ещё не рисовал.
Отсюда и ответ: информация берётся из настоящего DOM, уже приведённого в актуальное состояние, через реф и обычные браузерные методы. Вызов getBoundingClientRect заставляет браузер посчитать раскладку синхронно и возвращает свежие размеры.
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setOffset(height);
}, []);Смысл именно этого хука в том, что установка состояния внутри него вызывает повторный рендер и коммит до отрисовки, поэтому промежуточный кадр пользователь не увидит. То же измерение внутри useEffect даст заметное мелькание элемента на прежнем месте.
Где спотыкаются
Принимают посылку вопроса на веру и объясняют, как хук читает размеры до создания узла. До коммита читать нечего: рефа ещё нет, а размеры зависят от стилей, которые браузер пока не применял.
Ставят этот хук везде вместо useEffect. Он блокирует отрисовку, и цепочка из измерения, установки состояния и нового коммита на каждом обновлении превращается в заметные подтормаживания списка.
Забывают про серверный рендер. На сервере layout-эффекты не выполняются, React об этом предупреждает, и первый кадр приходит без измерений. Мерить стоит и позже: размеры после подгрузки шрифтов и картинок меняются, поэтому обычно нужен ResizeObserver.