UNPKG

@n0n3br/react-use-deep-compare-effect

Version:

React hook similar to useEffect but with deep comparison for dependencies.

167 lines (129 loc) 5.66 kB
# `@n0n3br/react-use-deep-compare` A React hook that works just like `useEffect`, but performs a deep comparison of its dependencies, preventing unnecessary re-runs when object or array references change but their content remains the same. ## 💡 Why `useDeepCompareEffect`? React's built-in `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. This can lead to: - **Unnecessary re-renders and computations:** Your effect might run more often than needed, impacting performance. - **Infinite loops:** If your effect updates state that causes a new object/array reference to be created, and that object/array is a dependency, you can easily end up in a loop. `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. ## Features - **Deep Equality Check:** Compares the actual values of objects and arrays, not just their references. - **Circular Reference Handling:** Safely handles objects with circular references to prevent infinite loops during comparison. - **Specific Type Handling:** Correctly compares `Date` objects by their time value and `RegExp` objects by their source and flags. - **Function Reference Comparison:** Like `useEffect`, functions are compared by reference, ensuring standard React behavior for callbacks. - **Type-Safe:** Built with TypeScript for a robust development experience. ## 🚀 Installation You can install `@n0n3br/use-deep-compare-effect` using npm, pnpm, or yarn. using npm npm install @n0n3br/use-deep-compare-effect using pnpm pnpm install @n0n3br/use-deep-compare-effect using yarn yarn add @n0n3br/use-deep-compare-effect ## 📖 Usage Using `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<string[]>([]); // 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 ( <div style={{ padding: "20px", border: "1px solid #ccc", borderRadius: "8px", }}> <h2>My Component with Deep Compare Effect</h2> <pre>{JSON.stringify(settings, null, 2)}</pre> <div style={{ display: "flex", gap: "10px", marginTop: "10px" }}> <button onClick={updateTheme}>Update Theme (Deep Change)</button> <button onClick={toggleEmailNotifications}> Toggle Email Notif. (Deep Change) </button> <button onClick={addTag}>Add Tag (Deep Change)</button> <button onClick={triggerShallowUpdate}> Trigger Shallow Update (No Deep Change) </button> </div> <h3>Effect Log:</h3> <div style={{ border: "1px solid #eee", padding: "10px", maxHeight: "150px", overflowY: "auto", }}> {effectLog.length === 0 ? ( <p>No effect runs yet...</p> ) : ( <ul> {effectLog.map((log, index) => ( <li key={index}>{log}</li> ))} </ul> )} </div> </div> ); } export default MyComponent; ``` ## 🤝 Contributing Contributions are welcome! If you find a bug or have an idea for an enhancement, please open an issue or submit a pull request. ## 📄 License This project is licensed under the MIT License - see the LICENSE file for details.