Hooks
No hooks found in any category.
useDeepCompareEffect
lifecycleInstallation
npx usehooks-cli@latest add use-deep-compare-effect
Description
A React hook that works like useEffect but performs deep comparison on dependencies instead of shallow comparison. Useful when dependencies are objects or arrays that might be recreated on each render.
Parameters
Name | Type | Default | Description |
---|---|---|---|
effect | React.EffectCallback | - | The effect function to run when dependencies change |
deps? | React.DependencyList | - | Array of dependencies to deep compare |
Examples
Object Dependencies
Using with object dependencies that are recreated on each render
1const [user, setUser] = useState({ name: 'John', age: 30 });
2const [settings, setSettings] = useState({ theme: 'dark', lang: 'en' });
3
4// This would run on every render with regular useEffect
5// because objects are recreated each time
6useDeepCompareEffect(() => {
7 console.log('User or settings changed');
8 updateUserProfile(user, settings);
9}, [user, settings]);
10
11// Only runs when the actual content of user or settings changes
12// not when the object references change
Array Dependencies
Preventing unnecessary effects with array dependencies
1const [filters, setFilters] = useState(['active', 'verified']);
2const [sortOrder, setSortOrder] = useState(['name', 'asc']);
3
4useDeepCompareEffect(() => {
5 // This effect only runs when the actual array contents change
6 // not when new array instances are created
7 fetchFilteredData(filters, sortOrder);
8}, [filters, sortOrder]);
9
10const addFilter = (newFilter) => {
11 // Even though this creates a new array, the effect won't run
12 // if the content is the same
13 setFilters(prev => [...prev, newFilter]);
14};
Complex Nested Objects
Deep comparison with nested object structures
1const [config, setConfig] = useState({
2 api: {
3 baseUrl: 'https://api.example.com',
4 timeout: 5000,
5 headers: { 'Content-Type': 'application/json' }
6 },
7 features: {
8 darkMode: true,
9 notifications: ['email', 'push']
10 }
11});
12
13useDeepCompareEffect(() => {
14 // Only runs when the actual configuration values change
15 // not when the config object is recreated
16 initializeApp(config);
17
18 return () => {
19 cleanupApp();
20 };
21}, [config]);
22
23// This won't trigger the effect if the values are the same
24const updateConfig = () => {
25 setConfig({
26 api: {
27 baseUrl: 'https://api.example.com', // same value
28 timeout: 5000, // same value
29 headers: { 'Content-Type': 'application/json' } // same value
30 },
31 features: {
32 darkMode: true, // same value
33 notifications: ['email', 'push'] // same array content
34 }
35 });
36};
Dependencies
react
Notes
- •Performs deep equality comparison on all dependency values
- •More expensive than regular useEffect due to deep comparison overhead
- •Use only when necessary - prefer restructuring to avoid object/array dependencies
- •Handles nested objects, arrays, and primitive values correctly
- •Comparison includes checking object keys, array lengths, and recursive value comparison
- •Memoizes dependencies to prevent unnecessary re-computations
Implementation
1"use client";
2
3import { useEffect, useRef } from "react";
4
5// Deep equality comparison function
6function deepEqual(a: any, b: any): boolean {
7 if (a === b) return true;
8
9 if (a == null || b == null) return a === b;
10
11 if (typeof a !== typeof b) return false;
12
13 if (typeof a !== "object") return a === b;
14
15 if (Array.isArray(a) !== Array.isArray(b)) return false;
16
17 if (Array.isArray(a)) {
18 if (a.length !== b.length) return false;
19 for (let i = 0; i < a.length; i++) {
20 if (!deepEqual(a[i], b[i])) return false;
21 }
22 return true;
23 }
24
25 const keysA = Object.keys(a);
26 const keysB = Object.keys(b);
27
28 if (keysA.length !== keysB.length) return false;
29
30 for (const key of keysA) {
31 if (!keysB.includes(key)) return false;
32 if (!deepEqual(a[key], b[key])) return false;
33 }
34
35 return true;
36}
37
38// Deep compare dependencies
39function useDeepCompareMemoize(value: React.DependencyList | undefined) {
40 const ref = useRef<React.DependencyList | undefined>(undefined);
41
42 if (!deepEqual(value, ref.current)) {
43 ref.current = value;
44 }
45
46 return ref.current;
47}
48
49export function useDeepCompareEffect(
50 effect: React.EffectCallback,
51 deps?: React.DependencyList
52): void {
53 const memoizedDeps = useDeepCompareMemoize(deps);
54
55 useEffect(effect, memoizedDeps);
56}
57
58export default useDeepCompareEffect;
59