Skip to content

useDeepCompareEffect

Works just like useEffect but with objects being compared by value not reference.

Installation

npx rabbithook@latest add use-deep-compare-effect

Usage

App.tsx
import useDeepCompareEffect from "@/hooks/use-deep-compare-effect";
function Component() {
const [user, setUser] = useState({
id: "any_id",
name: "any_name",
address: {
street: "any_street",
city: "any_city",
country: "any_country"
}
});
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Normal effect called with user: ", user);
}, [user]) // Will be called every time count changes
useDeepCompareEffect(() => {
console.log("Deep compared effect called with user: ", user);
}, [user]) // Will be called only when user changes
return (
<>
<button onClick={() => setCount(prevCount => prevCount++)}>Increase Counter</button>
<button onClick={() => setUser(prevUser => ({...prevUser, name: "new_name" }))}>
Update user
</button>
</>
)
}

Code

use-deep-compare-effect.ts
import { useRef, useEffect, EffectCallback } from "react"
type ICompareValue = Record<string, unknown>;
type ICompare= ICompareValue[] | ICompareValue;
function useDeepCompareEffect(callback: EffectCallback, dependencies: unknown[]) {
const currentDependenciesRef = useRef<unknown[]>([]);
if (!deepCompare(currentDependenciesRef.current as ICompare, dependencies as ICompare)) {
currentDependenciesRef.current = dependencies;
}
useEffect(callback, [currentDependenciesRef.current])
}
const deepCompare = (firstValue: ICompare, secondValue: ICompare) => {
if (!isObject(firstValue) || !isObject(secondValue)) return firstValue === secondValue;
const keys1 = Object.keys(firstValue);
const keys2 = Object.keys(secondValue);
if (keys1.length !== keys2.length) return false;
let areObjectsEqual = true;
keys1.forEach(key => {
const valueInObject1 = firstValue[key];
const valueInObject2 = secondValue[key];
const areObjects = isObject(valueInObject1) && isObject(valueInObject2);
const areEqual = areObjects ? deepCompare(valueInObject1 as ICompareValue, valueInObject2 as ICompareValue) : valueInObject1 === valueInObject2;
if (areEqual) {
areObjectsEqual = false;
}
})
return areObjectsEqual;
}
const isObject = (value: any) => value !== null && typeof value === "object";
export default useDeepCompareEffect;