--- name: react-useeffect-audit description: Identify and fix unnecessary useEffect usage in React code. Use when reviewing React code that uses Effects, when asked to optimize React components, when seeing patterns like "useEffect + setState", when users ask about Effect best practices, or when refactoring React code. Helps eliminate redundant renders and simplify component logic. --- # React useEffect Audit Effects are an escape hatch for synchronizing with **external systems** (DOM, network, third-party widgets). If no external system is involved, you likely don't need an Effect. ## Quick Decision Guide | Scenario | Use Effect? | Alternative | |----------|-------------|-------------| | Transform data for rendering | No | Calculate during render | | Handle user events | No | Event handler | | Cache expensive calculations | No | `useMemo` | | Reset state when props change | No | `key` prop | | Adjust state based on props | No | Calculate during render | | Chain of state updates | No | Single event handler | | Notify parent of state change | No | Call parent in event handler | | Initialize app (once) | No | Module-level code | | Subscribe to external store | No | `useSyncExternalStore` | | Fetch data on mount/change | Yes | With cleanup; prefer framework | ## Common Anti-Patterns ### 1. Derived State in Effect ```jsx // ❌ Bad: Extra render, unnecessary state const [fullName, setFullName] = useState(''); useEffect(() => { setFullName(firstName + ' ' + lastName); }, [firstName, lastName]); // ✅ Good: Calculate during render const fullName = firstName + ' ' + lastName; ``` ### 2. Event Logic in Effect ```jsx // ❌ Bad: Runs on mount if condition true, not just on click useEffect(() => { if (product.isInCart) { showNotification('Added to cart!'); } }, [product]); // ✅ Good: In event handler function handleBuyClick() { addToCart(product); showNotification('Added to cart!'); } ``` ### 3. Expensive Calculations ```jsx // ❌ Bad: Unnecessary state and Effect const [filtered, setFiltered] = useState([]); useEffect(() => { setFiltered(getFilteredTodos(todos, filter)); }, [todos, filter]); // ✅ Good: useMemo for expensive calculations const filtered = useMemo( () => getFilteredTodos(todos, filter), [todos, filter] ); ``` ### 4. Resetting State on Prop Change ```jsx // ❌ Bad: Effect to reset state function ProfilePage({ userId }) { const [comment, setComment] = useState(''); useEffect(() => { setComment(''); }, [userId]); } // ✅ Good: Use key to create fresh instance ``` ### 5. Effect Chains ```jsx // ❌ Bad: Multiple Effects triggering each other useEffect(() => { if (card?.gold) setGoldCount(c => c + 1); }, [card]); useEffect(() => { if (goldCount > 3) { setRound(r => r + 1); setGoldCount(0); } }, [goldCount]); // ✅ Good: Calculate in event handler function handlePlaceCard(nextCard) { setCard(nextCard); if (nextCard.gold) { if (goldCount < 3) setGoldCount(goldCount + 1); else { setGoldCount(0); setRound(round + 1); } } } ``` ### 6. Notifying Parent ```jsx // ❌ Bad: Effect to notify parent useEffect(() => { onChange(isOn); }, [isOn, onChange]); // ✅ Good: Notify in same event function handleClick() { setIsOn(!isOn); onChange(!isOn); } // ✅ Better: Controlled component (parent owns state) function Toggle({ isOn, onChange }) { return