useHooks.iov4.1.2
DocsBlogGitHub
Hooks
No hooks found in any category.

useDeepCompareEffect

lifecycle

Installation

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

NameTypeDefaultDescription
effectReact.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