useDeps
Описание
useDeps — хук, который выдаёт стабильный идентификатор (id) и увеличивает его, когда определённый набор зависимостей считается изменившимся. Поддерживает несколько форм вызова (перегрузки):
- Без аргументов —
idникогда не меняется (всегда0). - С массивом зависимостей — изменение определяется поверхностным сравнением по индексам (
===). - С функцией сравнения и значением — изменение определяется кастомным компаратором.
- Только с функцией сравнения — изменение определяется кастомной логикой, инкапсулированной в самой функции (например, через замыкание).
id удобен как «маяк» для перезапуска эффектов, сброса кэшей или повторной инициализации, когда сравнение сложнее стандартной ссылки.
Сигнатура
ts
// 1) Без аргументов — стабильный id (всегда 0)
function useDeps(): [id: number];
// 2) С массивом зависимостей — поверхностное сравнение (===) по индексам
function useDeps(deps: unknown[]): [id: number];
// 3) Кастомный компаратор + сравниваемое значение
function useDeps<ValueType>(
compare: (prev: ValueType, next: ValueType) => boolean, // вернуть true, если РАВНЫ (изменения НЕТ)
comparedValue: ValueType,
): [id: number];
// 4) Только компаратор — сравнение целиком внутри функции (например, через замыкание)
function useDeps(compare: () => boolean): [id: number]; // вернуть true, если РАВНЫ (изменения НЕТ)Параметры
deps?: unknown[]— массив зависимостей для поверхностного сравнения.compare?: (prev, next) => boolean— компаратор, который должен вернутьtrue, если значения равны (нет изменения), иfalse, если значения различаются (следовательно, нужно увеличитьid).comparedValue?: unknown— текущее значение для сравнения кастомным компаратором.
Возвращает:
[id: number]— кортеж с текущим стабильным идентификатором.
Примеры
1) Без аргументов (статический id)
tsx
const [id] = useDeps();
// id === 0 на всех рендерах2) Массив зависимостей (поверхностное сравнение)
tsx
const [reloadId] = useDeps([page, pageSize, query]);
useEffect(() => {
// выполнится только если page/pageSize/query реально поменялись по значению (===) и/или длине массива
fetchData({ page, pageSize, query });
}, [reloadId]);3) Кастомный компаратор + значение
tsx
// перезапуск, когда "смысл" пользователя изменился (id и роль не совпали)
const [userVersion] = useDeps(
(prev, next) => prev?.id === next?.id && prev?.role === next?.role,
currentUser,
);
useEffect(() => {
reinitUserSession(currentUser);
}, [userVersion]);4) Только компаратор (вся логика внутри функции)
tsx
// сравнение через замыкание: читаем что-то из внешней области (например, модульный синглтон)
const [stamp] = useDeps(() => cache.getHash() === lastAppliedHash.current);
useEffect(() => {
lastAppliedHash.current = cache.getHash();
warmUpCache();
}, [stamp]);Поведение
Базовая инициализация
- На первом рендере
idравен0и не увеличивается.
- На первом рендере
Определение изменения (массив deps)
- Используется поверхностное сравнение по индексам (
===) и длине массива. Если массивы не равны —idувеличивается на+1.
- Используется поверхностное сравнение по индексам (
Определение изменения (кастомный компаратор)
- Компаратор должен вернуть
true, если значения равны (изменения нет), иfalse, если значения различаются (изменение есть). При различииidувеличивается на+1.
- Компаратор должен вернуть
Ветка с компаратором без значения
- Функция сравнения вызывается на каждом рендере. Она должна сама решать, изменилось ли состояние (например, сверяя значения из замыкания). Возвращайте
trueпри равенстве,falseпри различии.
- Функция сравнения вызывается на каждом рендере. Она должна сама решать, изменилось ли состояние (например, сверяя значения из замыкания). Возвращайте
Стабильность
ididмонотонно возрастает (0, 1, 2, …) только при детектированном изменении; не обнуляется.
Стабильность ссылки на хук
useDepsне создаёт дополнительных функций/объектов в результатах; возвращаемый кортеж стабилен по форме, меняется только числоid.
Когда использовать
- Когда стандартного сравнения по ссылке недостаточно и нужно своё правило «что считать изменением».
- Для «маячка» перезапуска
useEffect/повторной инициализации, где зависимостей много или они сложные. - Чтобы объединить набор зависимостей в один целочисленный маркер и упростить зависимости других хуков/эффектов.
Когда не использовать
- Если можно напрямую положить зависимости в массив
useEffect— так проще и понятнее. - Если требуется глубокое сравнение сложных структур и оно всегда одно и то же — рассмотрите специализированный хук (например, deep‑compare effect) вместо кастомного компаратора каждый раз.
- Если вам нужен не счётчик изменений, а именно значение (или его мемоизация) — используйте
useMemo/useState.
Частые ошибки
Неверная семантика компаратора
- Компаратор должен возвращать
true, когда значения равны (нет изменения), иfalse, когда различаются. Инвертированная логика приведёт к постоянному инкрементуidили к его «заморозке».
- Компаратор должен возвращать
Переиспользование нового массива deps каждый рендер
- Если собирать массив из новых ссылок (например,
[...obj]), но значения по факту одинаковые, поверхностное сравнение всё равно корректно отработает по значениям. Однако следите за длиной/порядком — их изменение считается изменением.
- Если собирать массив из новых ссылок (например,
Нестабильный компаратор с замыканиями
- В форме
useDeps(compare)убедитесь, что логика сравнения не опирается на "дрожащие" ссылки без необходимости. Лучше хранить опорные значения вrefи обновлять их предсказуемо.
- В форме
Ожидание сброса
ididникогда не сбрасывается автоматически. Если нужен явный сброс, храните отдельный «поколенческий» счётчик в своём состоянии.