React hook similar to useEffect but with deep comparison for dependencies.
npm install @n0n3br/react-use-deep-compare-effect@n0n3br/react-use-deep-compareuseEffect, but performs a deep comparison of its dependencies, preventing unnecessary re-runs when object or array references change but their content remains the same.
useDeepCompareEffect?useEffect hook performs a shallow comparison of its dependencies. This means if you pass an object or an array as a dependency, useEffect will re-run the effect every time the object/array reference changes, even if its internal content is identical.
useDeepCompareEffect solves this by performing a deep comparison of your dependencies. It only re-runs your effect when the actual _content_ of your object or array dependencies changes, not just their reference.
Date objects by their time value and RegExp objects by their source and flags.
useEffect, functions are compared by reference, ensuring standard React behavior for callbacks.
@n0n3br/use-deep-compare-effect using npm, pnpm, or yarn.
useDeepCompareEffect is almost identical to using useEffect. Just import it and use it in place of useEffect.
typescript jsx
import React, { useState } from "react";
import { useDeepCompareEffect } from "use-deep-compare-effect"; // Adjust path if not installed as a package
function MyComponent() {
const [settings, setSettings] = useState({
theme: "dark",
notifications: {
email: true,
sms: false,
},
tags: ["react", "hook"],
});
const [effectLog, setEffectLog] = useState([]);
// This effect will only run when the DEEP content of 'settings' changes.
useDeepCompareEffect(() => {
const timestamp = new Date().toLocaleTimeString();
setEffectLog((prev) => [
...prev,
useDeepCompareEffect ran at: ${timestamp} (Settings changed deeply),
]);
console.log("Deeply compared effect ran:", settings);
// Optional cleanup function
return () => {
console.log("Deeply compared effect cleanup");
};
}, [settings]); // Dependencies are objects/arrays that need deep comparison
// --- For comparison: A standard useEffect ---
// This effect would run every time 'settings' reference changes,
// even if its content is the same.
// useEffect(() => {
// console.log('Standard useEffect ran:', settings);
// }, [settings]);
const updateTheme = () => {
setSettings((prev) => ({
...prev,
theme: prev.theme === "dark" ? "light" : "dark",
}));
};
const toggleEmailNotifications = () => {
setSettings((prev) => ({
...prev,
notifications: {
...prev.notifications,
email: !prev.notifications.email,
},
}));
};
const addTag = () => {
setSettings((prev) => ({
...prev,
tags: [...prev.tags, new-tag-${prev.tags.length + 1}],
}));
};
const triggerShallowUpdate = () => {
// This creates a NEW settings object reference, but with the SAME DEEP content
// useDeepCompareEffect WILL NOT run.
// Standard useEffect WOULD run.
setSettings((prev) => ({ ...prev }));
};
return (
style={{
padding: "20px",
border: "1px solid #ccc",
borderRadius: "8px",
}}>
My Component with Deep Compare Effect
{JSON.stringify(settings, null, 2)}
Effect Log:
style={{
border: "1px solid #eee",
padding: "10px",
maxHeight: "150px",
overflowY: "auto",
}}>
{effectLog.length === 0 ? (
No effect runs yet...
) : (
{effectLog.map((log, index) => (
- {log}
))}
)}