useEffectCompare is a wrapper around useEffect that executes the effect only when the dependencies have "logically" changed. Instead of React’s default reference comparison, you can provide:
- a dependency array with shallow index-based comparison (
===), - a custom comparator function with a separate value,
- or just a comparator function if the comparison logic is fully encapsulated (e.g., using closure state).
// 1) Dependency array
function useEffectCompare(
effect: () => void | (() => void),
deps: unknown[],
): void;
// 2) Custom comparator + value
function useEffectCompare<ComparedValue>(
effect: () => void | (() => void),
compare: (prev: ComparedValue, next: ComparedValue) => boolean,
comparedValue: ComparedValue,
): void;
// 3) Comparator only
function useEffectCompare(
effect: () => void | (() => void),
compare: () => boolean,
): void;-
Parameters
effect— the effect function; may return a cleanup function (same as inuseEffect).deps— dependency array; compared shallowly by index (===).compare— a function that returnstrueif values are considered equal (no change) andfalseif they differ (change detected).comparedValue— a value passed into the custom comparator.
-
Returns
void— just likeuseEffect.
import { useEffectCompare } from '@webeach/react-hooks/useEffectCompare';
function Search({ query, page, pageSize }: { query: string; page: number; pageSize: number }) {
useEffectCompare(() => {
// Runs only if query/page/pageSize actually changed in value
fetchResults({ query, page, pageSize });
}, [query, page, pageSize]);
return null;
}import { useEffectCompare } from '@webeach/react-hooks/useEffectCompare';
function Session({ user }: { user: { id: string; role: string } | null }) {
useEffectCompare(
() => {
// Restart session only if id or role has changed
reinitSession(user);
return () => disposeSession();
},
(prev, next) => prev?.id === next?.id && prev?.role === next?.role,
user,
);
return null;
}import { useEffectCompare } from '@webeach/react-hooks/useEffectCompare';
function CacheWarmup() {
useEffectCompare(
() => {
warmUpCache();
},
() => cache.getHash() === lastAppliedHash.current,
);
return null;
}-
Effect trigger
- The effect is executed only if a change is detected by the chosen comparison method.
- For arrays → shallow comparison by index and length.
- For custom comparator → executes
effectonly when comparator returnsfalse.
-
Dynamic dependency arrays
- The
depsarray can be dynamic: length and order may change between renders. Comparison accounts for both length and index-based values. UnlikeuseEffect, you don’t need to keep the array shape stable.
- The
-
Comparator semantics
- Must return
truewhen values are equal (no change) andfalsewhen they differ (effect should re-run).
- Must return
-
Cleanup
- The cleanup function returned from
effectis called before the next run and on unmount (same asuseEffect).
- The cleanup function returned from
-
Flexible forms
- Choose between dependency array, comparator + value, or comparator only — depending on your needs.
- When React’s default dependency comparison (
===) is insufficient. - To optimize effects so they run only on meaningful changes.
- When working with objects/arrays where you only care about specific fields.
- If default
useEffectwith a dependency array works fine. - If you need deep comparison of large/complex structures on every render (this can be expensive). Consider normalization or memoization instead.
-
Reversed comparator return
- The comparator must return
trueif values are equal. Returning the opposite leads to missed or excessive re-runs.
- The comparator must return
-
Creating new arrays/objects each render
- If you pass
[...obj]or{ ...obj }each render, the shallow compare will treat it as changed even if contents are the same.
- If you pass
-
Unstable comparator closures
- When using the comparator-only form, ensure it reads from stable refs. Otherwise, you might trigger unnecessary or skip required runs.
-
Expecting
useLayoutEffectbehavior- This hook wraps
useEffect. If you need synchronous before-paint execution, useuseLayoutEffectCompare.
- This hook wraps