Skip to content

useDeps

Описание

useDeps — хук, который выдаёт стабильный идентификатор (id) и увеличивает его, когда определённый набор зависимостей считается изменившимся. Поддерживает несколько форм вызова (перегрузки):

  1. Без аргументов — id никогда не меняется (всегда 0).
  2. С массивом зависимостей — изменение определяется поверхностным сравнением по индексам (===).
  3. С функцией сравнения и значением — изменение определяется кастомным компаратором.
  4. Только с функцией сравнения — изменение определяется кастомной логикой, инкапсулированной в самой функции (например, через замыкание).

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]);

Поведение

  1. Базовая инициализация

    • На первом рендере id равен 0 и не увеличивается.
  2. Определение изменения (массив deps)

    • Используется поверхностное сравнение по индексам (===) и длине массива. Если массивы не равны — id увеличивается на +1.
  3. Определение изменения (кастомный компаратор)

    • Компаратор должен вернуть true, если значения равны (изменения нет), и false, если значения различаются (изменение есть). При различии id увеличивается на +1.
  4. Ветка с компаратором без значения

    • Функция сравнения вызывается на каждом рендере. Она должна сама решать, изменилось ли состояние (например, сверяя значения из замыкания). Возвращайте true при равенстве, false при различии.
  5. Стабильность id

    • id монотонно возрастает (0, 1, 2, …) только при детектированном изменении; не обнуляется.
  6. Стабильность ссылки на хук

    • useDeps не создаёт дополнительных функций/объектов в результатах; возвращаемый кортеж стабилен по форме, меняется только число id.

Когда использовать

  • Когда стандартного сравнения по ссылке недостаточно и нужно своё правило «что считать изменением».
  • Для «маячка» перезапуска useEffect/повторной инициализации, где зависимостей много или они сложные.
  • Чтобы объединить набор зависимостей в один целочисленный маркер и упростить зависимости других хуков/эффектов.

Когда не использовать

  • Если можно напрямую положить зависимости в массив useEffect — так проще и понятнее.
  • Если требуется глубокое сравнение сложных структур и оно всегда одно и то же — рассмотрите специализированный хук (например, deep‑compare effect) вместо кастомного компаратора каждый раз.
  • Если вам нужен не счётчик изменений, а именно значение (или его мемоизация) — используйте useMemo/useState.

Частые ошибки

  1. Неверная семантика компаратора

    • Компаратор должен возвращать true, когда значения равны (нет изменения), и false, когда различаются. Инвертированная логика приведёт к постоянному инкременту id или к его «заморозке».
  2. Переиспользование нового массива deps каждый рендер

    • Если собирать массив из новых ссылок (например, [...obj]), но значения по факту одинаковые, поверхностное сравнение всё равно корректно отработает по значениям. Однако следите за длиной/порядком — их изменение считается изменением.
  3. Нестабильный компаратор с замыканиями

    • В форме useDeps(compare) убедитесь, что логика сравнения не опирается на "дрожащие" ссылки без необходимости. Лучше хранить опорные значения в ref и обновлять их предсказуемо.
  4. Ожидание сброса id

    • id никогда не сбрасывается автоматически. Если нужен явный сброс, храните отдельный «поколенческий» счётчик в своём состоянии.

Смотрите также