Guide on when to use and avoid useEffect in React applications. Learn patterns for state management, caching, and event handling.
Expert guidance on when to use and avoid `useEffect` in React applications, based on official React documentation patterns.
This skill helps you identify unnecessary Effects in React components and refactor them using appropriate patterns. Effects should only be used for synchronizing with external systems—not for transforming data or handling user events.
**Anti-pattern:**
```javascript
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
}
```
**Correct pattern:**
```javascript
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
// ✅ Calculate during rendering
const fullName = firstName + ' ' + lastName;
}
```
**Anti-pattern:**
```javascript
function BuyButton() {
useEffect(() => {
// Bad: Don't know which action triggered this
});
}
```
**Correct pattern:**
```javascript
function BuyButton() {
function handleClick() {
// ✅ Handle user action directly in event handler
fetch('/api/buy', { method: 'POST' });
showNotification('Purchase successful!');
}
return <button onClick={handleClick}>Buy</button>;
}
```
```javascript
useEffect(() => {
// ✅ Synchronize with external systems
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [serverUrl, roomId]);
```
**Before:**
```javascript
function TodoList({ todos, filter }) {
const [visibleTodos, setVisibleTodos] = useState([]);
useEffect(() => {
setVisibleTodos(getFilteredTodos(todos, filter));
}, [todos, filter]);
}
```
**After:**
```javascript
function TodoList({ todos, filter }) {
const visibleTodos = useMemo(() =>
getFilteredTodos(todos, filter),
[todos, filter]
);
}
```
**Before:**
```javascript
function ProfilePage({ userId }) {
const [comment, setComment] = useState('');
useEffect(() => {
setComment(''); // Reset on userId change
}, [userId]);
}
```
**After:**
```javascript
function ProfilePage({ userId }) {
return <Profile userId={userId} key={userId} />;
}
function Profile({ userId }) {
const [comment, setComment] = useState('');
// ✅ State resets automatically when key changes
}
```
**Before:**
```javascript
function List({ items }) {
const [selection, setSelection] = useState(null);
useEffect(() => {
setSelection(null); // Reset when items change
}, [items]);
}
```
**After:**
```javascript
function List({ items }) {
const [selection, setSelection] = useState(null);
const [prevItems, setPrevItems] = useState(items);
if (items !== prevItems) {
setPrevItems(items);
setSelection(null); // ✅ Adjust during render
}
}
```
**Before:**
```javascript
function ProductPage({ product }) {
useEffect(() => {
if (product.isInCart) {
showNotification(`Added ${product.name}`);
}
}, [product]);
}
```
**After:**
```javascript
function ProductPage({ product, onOpen }) {
function handleBuyClick() {
addToCart(product);
showNotification(`Added ${product.name}`);
}
function handleCheckoutClick() {
addToCart(product);
showNotification(`Added ${product.name}`);
navigateTo('/checkout');
}
}
```
**Before:**
```javascript
function Form() {
const [submitted, setSubmitted] = useState(false);
useEffect(() => {
if (submitted) {
post('/api/register');
}
}, [submitted]);
}
```
**After:**
```javascript
function Form() {
async function handleSubmit(e) {
e.preventDefault();
await post('/api/register'); // ✅ Direct in handler
navigateTo('/success');
}
}
```
When a user asks you to review React code or optimize Effects:
1. **Identify the Effect's purpose**
- Is it transforming data for rendering? → Remove Effect, calculate during render
- Is it handling a user event? → Move logic to event handler
- Is it synchronizing with an external system? → Keep Effect
2. **Check for redundant state**
- Can the value be calculated from existing props/state?
- Use direct calculation instead of state + Effect
3. **Optimize expensive calculations**
- Measure with `console.time()` first
- Wrap in `useMemo` only if calculation exceeds ~1ms
- Include all dependencies in the dependency array
4. **Handle state resets properly**
- Use `key` prop to reset component state automatically
- Adjust state during rendering for simple cases
- Avoid Effect-based resets
5. **Move event logic to handlers**
- POST requests belong in click handlers
- User notifications belong in event handlers
- Form submissions belong in `onSubmit` handlers
6. **Notify parent components correctly**
- Call parent callbacks during event handling, not in Effects
- Pass event handlers as props, not state setters
7. **Fetch data appropriately**
- Use framework data fetching when available (Next.js, Remix)
- Consider caching libraries (React Query, SWR)
- Use Effects only when necessary, with cleanup
❌ Chains of Effects updating state
❌ Effects that run on every render
❌ Effects that handle user interactions
❌ Effects that transform props to state
❌ Effects without proper cleanup
Based on official React documentation: [You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect)
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/react-effect-patterns/raw