useDeepCompareEffect
Like useEffect, but with deep comparison of dependencies. Prevents infinite re-renders when using objects or arrays as dependencies.
Live Demo
Compare regular useEffect vs useDeepCompareEffect. Notice how regular useEffect fires on every render, while useDeepCompareEffect only fires when values actually change.
📊 Effect Execution Count
Regular useEffect:0
useDeepCompareEffect:0
Efficiency Gain: 0% fewer executions
📝 Event Log
No events yet
👤 User Object
{
"name": "John",
"age": 25
}
💡 Notice:
- Regular useEffect fires on every render (even with same values)
- useDeepCompareEffect only fires when object content actually changes
- Click "Trigger Re-render" to see the difference
- This prevents infinite loops with object/array dependencies
Code Examples
The Problem with Regular useEffect
import { useEffect, useState } from "react";
// ❌ This will cause infinite re-renders!
export default function BadExample() {
const [data, setData] = useState([]);
useEffect(() => {
// This creates a new array reference every time
const filters = { status: 'active', limit: 10 };
fetchData(filters).then(setData);
}, [{ status: 'active', limit: 10 }]); // New object every render!
return <div>{data.length} items</div>;
}Solution with useDeepCompareEffect
import { useDeepCompareEffect } from "@uiblock/hooks";
import { useState } from "react";
// ✅ This works correctly!
export default function GoodExample() {
const [data, setData] = useState([]);
useDeepCompareEffect(() => {
const filters = { status: 'active', limit: 10 };
fetchData(filters).then(setData);
}, [{ status: 'active', limit: 10 }]); // Deep comparison!
return <div>{data.length} items</div>;
}Fetching Data with Complex Filters
import { useDeepCompareEffect } from "@uiblock/hooks";
import { useState } from "react";
interface Filters {
search: string;
categories: string[];
priceRange: { min: number; max: number };
}
export default function ProductList() {
const [products, setProducts] = useState([]);
const [filters, setFilters] = useState<Filters>({
search: '',
categories: ['electronics'],
priceRange: { min: 0, max: 1000 }
});
useDeepCompareEffect(() => {
fetchProducts(filters).then(setProducts);
}, [filters]);
return (
<div>
<input
value={filters.search}
onChange={(e) => setFilters({ ...filters, search: e.target.value })}
/>
{/* Products list */}
</div>
);
}Array Dependencies
import { useDeepCompareEffect } from "@uiblock/hooks";
import { useState } from "react";
export default function SelectedItems() {
const [selectedIds, setSelectedIds] = useState([1, 2, 3]);
const [items, setItems] = useState([]);
useDeepCompareEffect(() => {
// Only fetches when selectedIds content actually changes
fetchItemsByIds(selectedIds).then(setItems);
}, [selectedIds]);
return <div>{items.length} selected items</div>;
}How It Works
Here's the implementation:
import { useEffect, useRef, DependencyList, EffectCallback } from 'react'
function deepEqual(a: any, b: any): boolean {
if (a === b) return true
if (a == null || b == null) return false
if (typeof a !== 'object' || typeof b !== 'object') return false
const keysA = Object.keys(a)
const keysB = Object.keys(b)
if (keysA.length !== keysB.length) return false
for (const key of keysA) {
if (!keysB.includes(key)) return false
if (!deepEqual(a[key], b[key])) return false
}
return true
}
function useDeepCompareMemoize(value: DependencyList) {
const ref = useRef<DependencyList>([])
if (!deepEqual(value, ref.current)) {
ref.current = value
}
return ref.current
}
export function useDeepCompareEffect(
effect: EffectCallback,
dependencies: DependencyList
) {
useEffect(effect, useDeepCompareMemoize(dependencies))
}Key Features:
- Recursively compares object and array contents
- Only updates dependencies when content actually changes
- Prevents infinite re-render loops
- Works with nested objects and arrays
When to Use
✅ Good Use Cases
- Object or array dependencies
- Complex filter objects
- Configuration objects
- API request parameters
❌ Avoid When
- Dependencies are primitives
- Very large/deep objects
- High-frequency updates
- Can use useMemo instead
API Reference
Parameters
effect: EffectCallbackThe effect function to run (same as useEffect)
dependencies: DependencyListArray of dependencies (will be deeply compared)
Returns
void - Works exactly like useEffect
⚠️ Performance Note
Deep comparison has a performance cost. For very large or deeply nested objects, consider:
- Using useMemo to stabilize object references
- Breaking down into smaller, primitive dependencies
- Using a library like lodash.isEqual for more optimized comparison