useDeps is a hook that returns a stable identifier (id) and increments it whenever a given set of dependencies is considered changed. It supports multiple usage forms (overloads):
- No arguments —
idnever changes (always0). - With a dependency array — change is detected using shallow index-based comparison (
===). - With a comparator function and a value — change is detected by a custom comparator.
- With only a comparator function — change is detected by custom logic inside the comparator (e.g., using a closure).
id works as a convenient signal to restart effects, reset caches, or reinitialize logic when equality rules are more complex than simple reference checks.
// 1) No arguments — stable id (always 0)
function useDeps(): [id: number];
// 2) Dependency array — shallow (===) index-based comparison
function useDeps(deps: unknown[]): [id: number];
// 3) Custom comparator + compared value
function useDeps<ValueType>(
compare: (prev: ValueType, next: ValueType) => boolean, // return true if EQUAL (no change)
comparedValue: ValueType,
): [id: number];
// 4) Comparator only — all comparison logic inside the function (e.g., closure)
function useDeps(compare: () => boolean): [id: number]; // return true if EQUAL (no change)-
Parameters
deps?: unknown[]— dependency array for shallow comparison.compare?: (prev, next) => boolean— comparator that returnstrueif values are equal (no change), orfalseif they differ (incrementid).comparedValue?: unknown— value passed to the custom comparator.
-
Returns:
[id: number]— a tuple containing the current stable identifier.
const [id] = useDeps();
// id === 0 for all rendersconst [reloadId] = useDeps([page, pageSize, query]);
useEffect(() => {
// runs only if page/pageSize/query changed in value (===) or array length changed
fetchData({ page, pageSize, query });
}, [reloadId]);// Re-run only when "meaning" of user changes (id or role differ)
const [userVersion] = useDeps(
(prev, next) => prev?.id === next?.id && prev?.role === next?.role,
currentUser,
);
useEffect(() => {
reinitUserSession(currentUser);
}, [userVersion]);// Comparison via closure: read something external (e.g., cache hash)
const [stamp] = useDeps(() => cache.getHash() === lastAppliedHash.current);
useEffect(() => {
lastAppliedHash.current = cache.getHash();
warmUpCache();
}, [stamp]);-
Basic initialization
- On the first render,
idis0and does not increment until a change is detected.
- On the first render,
-
Change detection with
depsarray- Shallow comparison by index (
===) and array length. If any item differs,idincrements.
- Shallow comparison by index (
-
Change detection with comparator
- Comparator should return
trueif values are considered equal (no change), orfalseif they differ (change detected). On difference,idincrements.
- Comparator should return
-
Comparator without value
- The function is called on every render and must decide if a change occurred based on closure state. Return
truefor equality,falsefor difference.
- The function is called on every render and must decide if a change occurred based on closure state. Return
-
Stable counter
idincreases monotonically (0, 1, 2, …). It never resets automatically.
-
Stable return
- The hook always returns a tuple with the same shape; only
idchanges.
- The hook always returns a tuple with the same shape; only
- When default
useEffectdependency comparison (===) is not enough. - As a simple change signal for effects, caches, or reinitialization.
- To unify multiple or complex dependencies into a single numeric marker.
- If a plain
useEffectdependency array is sufficient. - If you always need deep equality — use
useEffectCompareinstead. - If you need the actual derived value (not just an incrementing id) — use
useMemoor another state hook.
-
Wrong comparator logic
- Comparator must return
truewhen values are equal. Returning the opposite will cause unnecessary or missing increments.
- Comparator must return
-
Recreating dependency arrays every render
- If you spread or build a new array each render (
[...obj]), shallow compare will treat it as changed if references differ.
- If you spread or build a new array each render (
-
Expecting
idto reset- The counter never resets automatically. If you need a reset, manage your own state.
-
Ignoring closure issues
- When using
useDeps(compare), make sure your comparator logic reads from stable refs to avoid false positives.
- When using