Stop Chaining useEffect: The Right Way to Handle Derived State in React
The Anti-Pattern: Syncing State via Effects
A frequent pitfall in dashboard and list components looks like this:


JavaScript
// ❌ Redundant state + extra render cycle
function SearchableList({ items }) {
const [filter, setFilter] = useState('');
const [filteredItems, setFilteredItems] = useState(items);


useEffect(() => {
setFilteredItems(
items.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()))
);
}, [items, filter]);


return (
<div>
<input value={filter} onChange={e => setFilter(e.target.value)} />
<ul>{filteredItems.map(item => <li key={item.id}>{item.name}</li>)}</ul>
</div>
);
}
Why this hurts:


The component renders once with stale filteredItems.


The useEffect fires after paint, updating state.


The component re-renders a second time with the filtered result.


You risk race conditions and hard-to-trace infinite loops as props scale.


The Clean Approach: Compute During Rendering
Calculate the value directly inside the component body:


JavaScript
// ✅ Zero redundant state, single render pass
function SearchableList({ items }) {
const [filter, setFilter] = useState('');


// Computed inline on every render
const filteredItems = items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);


return (
<div>
<input value={filter} onChange={e => setFilter(e.target.value)} />
<ul>{filteredItems.map(item => <li key={item.id}>{item.name}</li>)}</ul>
</div>
);
}
When to Bring in useMemo
Don't reach for useMemo preemptively for arrays under a few thousand items—JavaScript filtering is extremely fast. Only wrap the computation if profiling shows measurable lag:


JavaScript
const filteredItems = useMemo(() => {
return items.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()));
}, [items, filter]);
Key Takeaways


Never store derived data in state: If a value is computable from existing state or props, calculate it inline.


Cut redundant render passes: Updating state inside a useEffect on prop change forces an unnecessary second render cycle.


Keep effects for synchronization: Reserve useEffect strictly for external APIs, DOM mutations, subscriptions, or WebSocket listeners.


CTA
Where does state management get messy in your current codebase? Drop a snippet of a tricky useEffect or state dependency you're refactoring, and let's optimize it together in the comments!
Stop Chaining useEffect: The Right Way to Handle Derived State in React The Anti-Pattern: Syncing State via Effects A frequent pitfall in dashboard and list components looks like this: JavaScript // ❌ Redundant state + extra render cycle function SearchableList({ items }) { const [filter, setFilter] = useState(''); const [filteredItems, setFilteredItems] = useState(items); useEffect(() => { setFilteredItems( items.filter(item => item.name.toLowerCase().includes(filter.toLowerCase())) ); }, [items, filter]); return ( <div> <input value={filter} onChange={e => setFilter(e.target.value)} /> <ul>{filteredItems.map(item => <li key={item.id}>{item.name}</li>)}</ul> </div> ); } Why this hurts: The component renders once with stale filteredItems. The useEffect fires after paint, updating state. The component re-renders a second time with the filtered result. You risk race conditions and hard-to-trace infinite loops as props scale. The Clean Approach: Compute During Rendering Calculate the value directly inside the component body: JavaScript // ✅ Zero redundant state, single render pass function SearchableList({ items }) { const [filter, setFilter] = useState(''); // Computed inline on every render const filteredItems = items.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()) ); return ( <div> <input value={filter} onChange={e => setFilter(e.target.value)} /> <ul>{filteredItems.map(item => <li key={item.id}>{item.name}</li>)}</ul> </div> ); } When to Bring in useMemo Don't reach for useMemo preemptively for arrays under a few thousand items—JavaScript filtering is extremely fast. Only wrap the computation if profiling shows measurable lag: JavaScript const filteredItems = useMemo(() => { return items.filter(item => item.name.toLowerCase().includes(filter.toLowerCase())); }, [items, filter]); Key Takeaways Never store derived data in state: If a value is computable from existing state or props, calculate it inline. Cut redundant render passes: Updating state inside a useEffect on prop change forces an unnecessary second render cycle. Keep effects for synchronization: Reserve useEffect strictly for external APIs, DOM mutations, subscriptions, or WebSocket listeners. CTA Where does state management get messy in your current codebase? Drop a snippet of a tricky useEffect or state dependency you're refactoring, and let's optimize it together in the comments!
0 Yorumlar 0 hisse senetleri 43 Views 0 önizleme