useLayoutEffectCompare
Описание
useLayoutEffectCompare — обёртка над layout‑эффектом, которая запускает эффект только при «логическом» изменении зависимостей. Поддерживает три формы сравнения:
- массив зависимостей с поверхностным сравнением по индексам (
===), - кастомная функция сравнения и отдельное значение,
- только функция сравнения, если логика сравнения инкапсулирована внутри неё.
Эффект выполняется синхронно на этапе layout (до отрисовки), поэтому подходит для чтения/записи измерений DOM и синхронной подготовки к пейнту.
Сигнатура
ts
// 1) Массив зависимостей
function useLayoutEffectCompare(
effect: () => void | (() => void),
deps: unknown[],
): void;
// 2) Кастомный компаратор + значение
function useLayoutEffectCompare<ComparedValue>(
effect: () => void | (() => void),
compare: (prev: ComparedValue, next: ComparedValue) => boolean,
comparedValue: ComparedValue,
): void;
// 3) Только компаратор
function useLayoutEffectCompare(
effect: () => void | (() => void),
compare: () => boolean,
): void;Параметры
effect— функция layout‑эффекта; может вернуть функцию очистки.deps— массив зависимостей; сравнивается поверхностно по индексам (===).compare— функция сравнения, которая должна вернутьtrue, если значения равны (изменения нет), иfalse, если различаются (изменение есть).comparedValue— значение для сравнения пользовательским компаратором.
Возвращает
void— как и обычные эффекты.
Примеры
1) Динамические зависимости для синхронных измерений
tsx
import { useLayoutEffectCompare } from '@webeach/react-hooks';
function Measure({ nodes }: { nodes: HTMLElement[] }) {
useLayoutEffectCompare(() => {
// Выполнится только при реальном изменении массива nodes (значения/порядок/длина)
const bounds = nodes.map((element) => element.getBoundingClientRect());
syncLayout(bounds);
}, nodes);
return null;
}2) Кастомный компаратор по «смыслу» размеров
tsx
import { useLayoutEffectCompare } from '@webeach/react-hooks';
type Size = { width: number; height: number } | null;
function ResizeConsumer({ size }: { size: Size }) {
useLayoutEffectCompare(
() => {
applySize(size);
},
(prev, next) =>
prev?.width === next?.width && prev?.height === next?.height,
size,
);
return null;
}3) Только компаратор (логика в замыкании)
tsx
import { useLayoutEffectCompare } from '@webeach/react-hooks';
function GridRuler() {
useLayoutEffectCompare(
() => {
drawRulers();
return () => eraseRulers();
},
() => {
return layoutCache.version === lastVersionRef.current;
},
);
return null;
}Поведение
Триггер эффекта
- Эффект выполняется только при детектированном изменении зависимостей. Для массива — поверхностное сравнение по индексам; для компаратора — он должен вернуть
falseпри изменении.
- Эффект выполняется только при детектированном изменении зависимостей. Для массива — поверхностное сравнение по индексам; для компаратора — он должен вернуть
Динамический массив зависимостей
- В форме с
depsмассив может быть полностью динамическим: длина и порядок могут меняться от рендера к рендеру. Сравнение учитывает как значения по индексам, так и длину; изменение длины тоже считается изменением. В отличие от стандартногоuseLayoutEffect, не требуется поддерживать «строго одинаковую» длину массива между рендерами.
- В форме с
Layout‑семантика
- Эффект исполняется синхронно перед пейнтом, что удобно для измерений/синхронизации DOM. Используйте ответственно: долгие операции могут задержать отрисовку.
Очистка эффекта
- Функция очистки, возвращённая из
effect, вызывается перед следующим запуском эффекта и при размонтировании.
- Функция очистки, возвращённая из
Гибкость форм вызова
- Можно использовать массив зависимостей, пользовательский компаратор с отдельным значением или только компаратор — выбирайте форму под задачу.
Когда использовать
- Синхронные измерения DOM перед пейнтом: вычисление размеров, позиционирование, прокрутка.
- Когда важны значимые изменения зависимостей, а не просто смена ссылок.
- Сложные объекты зависимостей, где нужен компаратор по «смыслу».
Когда не использовать
- Если достаточно обычного
useEffect(после пейнта) и нет требований к синхронности. - Если тяжёлые вычисления в layout‑эффекте могут блокировать отрисовку — перенесите их на этап после пейнта или оптимизируйте.
Частые ошибки
Инвертированная логика компаратора
- Компаратор должен возвращать
trueпри равенстве иfalseпри различии. Иначе эффект будет запускаться лишний раз или пропускаться.
- Компаратор должен возвращать
Ежерендерная сборка массива зависимостей
- Если собирать новый массив с новыми ссылками каждый рендер, учитывайте, что меняющиеся порядок/длина также считаются изменением — эффект перезапустится.
Чрезмерно тяжёлый layout‑эффект
- Помните, что layout‑эффект выполняется синхронно до пейнта. Избегайте тяжёлых операций внутри него.