useEffectCompare
Описание
useEffectCompare — обёртка над useEffect, которая запускает эффект только при «логическом» изменении зависимостей. Вместо стандартного сравнения по ссылке можно задать:
- массив зависимостей с поверхностным сравнением по индексам (
===), - кастомную функцию сравнения и отдельное значение,
- только функцию сравнения, если логика сравнения инкапсулирована внутри неё (через замыкание и т.п.).
Сигнатура
ts
// 1) Массив зависимостей
function useEffectCompare(
effect: () => void | (() => void),
deps: unknown[],
): void;
// 2) Кастомный компаратор + значение
function useEffectCompare<ComparedValue>(
effect: () => void | (() => void),
compare: (prev: ComparedValue, next: ComparedValue) => boolean,
comparedValue: ComparedValue,
): void;
// 3) Только компаратор
function useEffectCompare(
effect: () => void | (() => void),
compare: () => boolean,
): void;Параметры
effect— функция эффекта; может вернуть функцию очистки (как вuseEffect).deps— массив зависимостей; сравнивается поверхностно по индексам (===).compare— функция сравнения, которая должна вернутьtrue, если значения равны (изменения нет), иfalse, если различаются (изменение есть).comparedValue— значение для сравнения пользовательским компаратором.
Возвращает
void— как иuseEffect.
Примеры
1) Массив зависимостей с поверхностным сравнением
tsx
import { useEffectCompare } from '@webeach/react-hooks';
function Search({
query,
page,
pageSize,
}: {
query: string;
page: number;
pageSize: number;
}) {
useEffectCompare(() => {
// Выполнится только при реальном изменении значений query/page/pageSize
fetchResults({ query, page, pageSize });
}, [query, page, pageSize]);
return null;
}2) Кастомный компаратор по "смыслу" объекта
tsx
import { useEffectCompare } from '@webeach/react-hooks';
function Session({ user }: { user: { id: string; role: string } | null }) {
useEffectCompare(
() => {
// Перезапускаем сессию только если id или роль поменялись по сути
reinitSession(user);
return () => disposeSession();
},
(prev, next) => prev?.id === next?.id && prev?.role === next?.role,
user,
);
return null;
}3) Только компаратор (логика через замыкание)
tsx
import { useEffectCompare } from '@webeach/react-hooks';
function CacheWarmup() {
useEffectCompare(
() => {
warmUpCache();
},
() => cache.getHash() === lastAppliedHash.current,
);
return null;
}Поведение
Триггер эффекта
- Эффект выполняется только при детектированном изменении зависимостей. Для массива — поверхностное сравнение по индексам; для компаратора — он должен вернуть
falseпри изменении.
- Эффект выполняется только при детектированном изменении зависимостей. Для массива — поверхностное сравнение по индексам; для компаратора — он должен вернуть
Динамический массив зависимостей
- В форме с
depsмассив может быть полностью динамическим: длина и порядок могут меняться от рендера к рендеру. Сравнение учитывает как значения по индексам, так и длину; изменение длины тоже считается изменением. В отличие от стандартногоuseEffect, не требуется поддерживать «строго одинаковую» длину массива между рендерами.
- В форме с
Семантика компаратора
- Возвращайте
true, если значения равны (изменения нет), иfalse, если различаются (изменение есть). Это влияет на то, будет ли перезапущен эффект.
- Возвращайте
Очистка эффекта
- Функция очистки, возвращённая из
effect, вызывается перед следующим запуском эффекта и при размонтировании компонента.
- Функция очистки, возвращённая из
Гибкость форм вызова
- Вы можете использовать массив зависимостей, пользовательский компаратор с отдельным значением или только компаратор — выбирайте форму под задачу.
Когда использовать
- Когда сравнение «по ссылке» недостаточно и нужно правило «что считать изменением».
- Для оптимизации эффектов, чтобы они перезапускались только при значимых изменениях.
- При работе с объектами, где важны конкретные поля, а не вся ссылка целиком.
Когда не использовать
- Если стандартного
useEffectс массивом зависимостей достаточно. - Если требуется глубокое сравнение тяжёлых структур на каждом рендере — подумайте о пересмотре архитектуры или о мемоизации/нормализации данных.
Частые ошибки
Инвертированная логика компаратора
- Компаратор должен возвращать
trueпри равенстве иfalseпри различии. Перепутанная семантика приведёт к пропущенным или лишним перезапускам эффекта.
- Компаратор должен возвращать
Ежерендерная сборка массива зависимостей
- Если формировать новый массив с новыми ссылками каждый рендер (например,
[{...obj}]), сравнение всё равно будет по значениям примитивов; следите за порядком/длиной — их изменение считается изменением.
- Если формировать новый массив с новыми ссылками каждый рендер (например,
Хрупкий компаратор с замыканиями
- В форме
useEffectCompare(effect, compare)убедитесь, что значения, используемые внутриcompare, стабильны и осознанно обновляются; иначе эффект может запускаться случайно или не запускаться вовсе.
- В форме
Ожидание работы как
useLayoutEffect- Это обёртка над
useEffect. Для синхронных до‑пейнта побочных эффектов используйтеuseLayoutEffectCompare.
- Это обёртка над