
React Useeffect
- 3.8k installs
- 2.2k repo stars
- Updated March 5, 2026
- softaworks/agent-toolkit
A reference skill encoding React official best practices for useEffect, explaining when to use it, when to avoid it, and which alternatives to apply instead.
About
This skill encodes React official documentation guidance on useEffect best practices, helping developers identify when Effects are necessary versus when simpler alternatives exist. It covers the core principle that Effects are an escape hatch for synchronizing with external systems, not a general-purpose state management tool. Developers use it when writing or reviewing code that involves useEffect, useState for derived values, data fetching, or component state synchronization. Key workflows include a quick-reference table mapping common anti-patterns to correct alternatives, a decision tree for choosing between event handlers, Effects, useMemo, and the key prop, and pointers to anti-patterns and alternative pattern docs. It also covers when Effects are legitimately required, such as browser API subscriptions, non-React widget integration, and analytics logging. Quick-reference table maps six common useEffect anti-patterns to their correct React alternatives
- Quick-reference table maps six common useEffect anti-patterns to their correct React alternatives
- Decision tree guides developers through choosing event handlers, Effects, useMemo, or key prop
- Covers legitimate Effect use cases: external system sync, subscriptions, analytics, data fetching with cleanup
- Teaches inline derived state calculation instead of useState plus useEffect combos
- Links to companion docs for anti-patterns, useMemo, key prop, lifting state, and useSyncExternalStore
React Useeffect by the numbers
- 3,762 all-time installs (skills.sh)
- +19 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #124 of 2,277 Frontend Development skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
react-useeffect capabilities & compatibility
- Capabilities
- useeffect pattern review · derived state refactoring guidance · data fetching pattern recommendation · event handler vs effect disambiguation · usememo applicability check
- Use cases
- code review · debugging · refactoring · frontend
- Runs
- Runs locally
- Pricing
- Free
What react-useeffect says it does
Use when writing/reviewing useEffect, useState for derived values, data fetching, or state synchronization.
npx skills add https://github.com/softaworks/agent-toolkit --skill react-useeffectAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3.8k |
|---|---|
| repo stars | ★ 2.2k |
| Security audit | 3 / 3 scanners passed |
| Last updated | March 5, 2026 |
| Repository | softaworks/agent-toolkit ↗ |
What it does
Guide developers on when to use or avoid useEffect in React, covering derived state, data fetching, and state synchronization patterns.
Who is it for?
React developers writing or reviewing functional components that involve useEffect, useState, or data fetching logic.
Skip if: Developers working outside React or those needing class component lifecycle guidance.
When should I use this skill?
A developer is writing a useEffect, questioning whether an Effect is needed, or reviewing React component code for hook misuse.
What you get
Developers write React components that avoid unnecessary Effects, use event handlers and inline calculations correctly, and apply useMemo or the key prop where appropriate.
- Identification of unnecessary useEffect usage in code review
- Correct alternative pattern for each common anti-pattern
- Decision tree output mapping a scenario to the right React primitive
By the numbers
- 6 anti-pattern scenarios covered in the quick-reference table
- 4 legitimate Effect use cases documented
- 4 decision tree branches for hook selection
Files
You Might Not Need an Effect
Effects are an escape hatch from React. They let you synchronize with external systems. If there is no external system involved, you shouldn't need an Effect.
Quick Reference
| Situation | DON'T | DO |
|---|---|---|
| Derived state from props/state | useState + useEffect | Calculate during render |
| Expensive calculations | useEffect to cache | useMemo |
| Reset state on prop change | useEffect with setState | key prop |
| User event responses | useEffect watching state | Event handler directly |
| Notify parent of changes | useEffect calling onChange | Call in event handler |
| Fetch data | useEffect without cleanup | useEffect with cleanup OR framework |
When You DO Need Effects
- Synchronizing with external systems (non-React widgets, browser APIs)
- Subscriptions to external stores (use
useSyncExternalStorewhen possible) - Analytics/logging that runs because component displayed
- Data fetching with proper cleanup (or use framework's built-in mechanism)
When You DON'T Need Effects
1. Transforming data for rendering - Calculate at top level, re-runs automatically 2. Handling user events - Use event handlers, you know exactly what happened 3. Deriving state - Just compute it: const fullName = firstName + ' ' + lastName 4. Chaining state updates - Calculate all next state in the event handler
Decision Tree
Need to respond to something?
├── User interaction (click, submit, drag)?
│ └── Use EVENT HANDLER
├── Component appeared on screen?
│ └── Use EFFECT (external sync, analytics)
├── Props/state changed and need derived value?
│ └── CALCULATE DURING RENDER
│ └── Expensive? Use useMemo
└── Need to reset state when prop changes?
└── Use KEY PROP on componentDetailed Guidance
- Anti-Patterns - Common mistakes with fixes
- Better Alternatives - useMemo, key prop, lifting state, useSyncExternalStore
Better Alternatives to useEffect
1. Calculate During Render (Derived State)
For values derived from props or state, just compute them:
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
// Runs every render - that's fine and intentional
const fullName = firstName + ' ' + lastName;
const isValid = firstName.length > 0 && lastName.length > 0;
}When to use: The value can be computed from existing props/state.
---
2. useMemo for Expensive Calculations
When computation is expensive, memoize it:
import { useMemo } from 'react';
function TodoList({ todos, filter }) {
const visibleTodos = useMemo(
() => getFilteredTodos(todos, filter),
[todos, filter]
);
}How to know if it's expensive:
console.time('filter');
const visibleTodos = getFilteredTodos(todos, filter);
console.timeEnd('filter');
// If > 1ms, consider memoizingNote: React Compiler can auto-memoize, reducing manual useMemo needs.
---
3. Key Prop to Reset State
To reset ALL state when a prop changes, use key:
// Parent passes userId as key
function ProfilePage({ userId }) {
return (
<Profile
userId={userId}
key={userId} // Different userId = different component instance
/>
);
}
function Profile({ userId }) {
// All state here resets when userId changes
const [comment, setComment] = useState('');
const [likes, setLikes] = useState([]);
}When to use: You want a "fresh start" when an identity prop changes.
---
4. Store ID Instead of Object
To preserve selection when list changes:
// BAD: Storing object that needs Effect to "adjust"
function List({ items }) {
const [selection, setSelection] = useState(null);
useEffect(() => {
setSelection(null); // Reset when items change
}, [items]);
}
// GOOD: Store ID, derive object
function List({ items }) {
const [selectedId, setSelectedId] = useState(null);
// Derived - no Effect needed
const selection = items.find(item => item.id === selectedId) ?? null;
}Benefit: If item with selectedId exists in new list, selection preserved.
---
5. Event Handlers for User Actions
User clicks/submits/drags should be handled in event handlers, not Effects:
// Event handler knows exactly what happened
function ProductPage({ product, addToCart }) {
function handleBuyClick() {
addToCart(product);
showNotification(`Added ${product.name}!`);
analytics.track('product_added', { id: product.id });
}
function handleCheckoutClick() {
addToCart(product);
showNotification(`Added ${product.name}!`);
navigateTo('/checkout');
}
}Shared logic: Extract a function, call from both handlers:
function buyProduct() {
addToCart(product);
showNotification(`Added ${product.name}!`);
}
function handleBuyClick() { buyProduct(); }
function handleCheckoutClick() { buyProduct(); navigateTo('/checkout'); }---
6. useSyncExternalStore for External Stores
For subscribing to external data (browser APIs, third-party stores):
// Instead of manual Effect subscription
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function update() { setIsOnline(navigator.onLine); }
window.addEventListener('online', update);
window.addEventListener('offline', update);
return () => {
window.removeEventListener('online', update);
window.removeEventListener('offline', update);
};
}, []);
return isOnline;
}
// Use purpose-built hook
import { useSyncExternalStore } from 'react';
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}
function useOnlineStatus() {
return useSyncExternalStore(
subscribe,
() => navigator.onLine, // Client value
() => true // Server value (SSR)
);
}---
7. Lifting State Up
When two components need synchronized state, lift it to common ancestor:
// Instead of syncing via Effects between siblings
function Parent() {
const [value, setValue] = useState('');
return (
<>
<Input value={value} onChange={setValue} />
<Preview value={value} />
</>
);
}---
8. Custom Hooks for Data Fetching
Extract fetch logic with proper cleanup:
function useData(url) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let ignore = false;
setLoading(true);
fetch(url)
.then(res => res.json())
.then(json => {
if (!ignore) {
setData(json);
setError(null);
}
})
.catch(err => {
if (!ignore) setError(err);
})
.finally(() => {
if (!ignore) setLoading(false);
});
return () => { ignore = true; };
}, [url]);
return { data, error, loading };
}
// Usage
function SearchResults({ query }) {
const { data, error, loading } = useData(`/api/search?q=${query}`);
}Better: Use framework's data fetching (React Query, SWR, Next.js, etc.)
---
Summary: When to Use What
| Need | Solution |
|---|---|
| Value from props/state | Calculate during render |
| Expensive calculation | useMemo |
| Reset all state on prop change | key prop |
| Respond to user action | Event handler |
| Sync with external system | useEffect with cleanup |
| Subscribe to external store | useSyncExternalStore |
| Share state between components | Lift state up |
| Fetch data | Custom hook with cleanup / framework |
useEffect Anti-Patterns
1. Redundant State for Derived Values
// BAD: Extra state + Effect for derived value
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
}
// GOOD: Calculate during rendering
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
const fullName = firstName + ' ' + lastName; // Just compute it
}Why it's bad: Causes extra render pass with stale value, then re-renders with updated value.
---
2. Filtering/Transforming Data in Effect
// BAD: Effect to filter list
function TodoList({ todos, filter }) {
const [visibleTodos, setVisibleTodos] = useState([]);
useEffect(() => {
setVisibleTodos(getFilteredTodos(todos, filter));
}, [todos, filter]);
}
// GOOD: Filter during render (memoize if expensive)
function TodoList({ todos, filter }) {
const visibleTodos = useMemo(
() => getFilteredTodos(todos, filter),
[todos, filter]
);
}---
3. Resetting State on Prop Change
// BAD: Effect to reset state
function ProfilePage({ userId }) {
const [comment, setComment] = useState('');
useEffect(() => {
setComment('');
}, [userId]);
}
// GOOD: Use key prop
function ProfilePage({ userId }) {
return <Profile userId={userId} key={userId} />;
}
function Profile({ userId }) {
const [comment, setComment] = useState(''); // Resets automatically
}Why key works: React treats components with different keys as different components, recreating state.
---
4. Event-Specific Logic in Effect
// BAD: Effect for button click result
function ProductPage({ product, addToCart }) {
useEffect(() => {
if (product.isInCart) {
showNotification(`Added ${product.name}!`);
}
}, [product]);
function handleBuyClick() {
addToCart(product);
}
}
// GOOD: Handle in event handler
function ProductPage({ product, addToCart }) {
function handleBuyClick() {
addToCart(product);
showNotification(`Added ${product.name}!`);
}
}Why it's bad: Effect fires on page refresh (isInCart is true), showing notification unexpectedly.
---
5. Chains of Effects
// BAD: Effects triggering each other
function Game() {
const [card, setCard] = useState(null);
const [goldCardCount, setGoldCardCount] = useState(0);
const [round, setRound] = useState(1);
const [isGameOver, setIsGameOver] = useState(false);
useEffect(() => {
if (card?.gold) setGoldCardCount(c => c + 1);
}, [card]);
useEffect(() => {
if (goldCardCount > 3) {
setRound(r => r + 1);
setGoldCardCount(0);
}
}, [goldCardCount]);
useEffect(() => {
if (round > 5) setIsGameOver(true);
}, [round]);
}
// GOOD: Calculate in event handler
function Game() {
const [card, setCard] = useState(null);
const [goldCardCount, setGoldCardCount] = useState(0);
const [round, setRound] = useState(1);
const isGameOver = round > 5; // Derived!
function handlePlaceCard(nextCard) {
if (isGameOver) throw Error('Game ended');
setCard(nextCard);
if (nextCard.gold) {
if (goldCardCount < 3) {
setGoldCardCount(goldCardCount + 1);
} else {
setGoldCardCount(0);
setRound(round + 1);
if (round === 5) alert('Good game!');
}
}
}
}Why it's bad: Multiple re-renders (setCard -> setGoldCardCount -> setRound -> setIsGameOver). Also fragile for features like history replay.
---
6. Notifying Parent via Effect
// BAD: Effect to notify parent
function Toggle({ onChange }) {
const [isOn, setIsOn] = useState(false);
useEffect(() => {
onChange(isOn);
}, [isOn, onChange]);
function handleClick() {
setIsOn(!isOn);
}
}
// GOOD: Notify in same event
function Toggle({ onChange }) {
const [isOn, setIsOn] = useState(false);
function updateToggle(nextIsOn) {
setIsOn(nextIsOn);
onChange(nextIsOn); // Same event, batched render
}
function handleClick() {
updateToggle(!isOn);
}
}
// BEST: Fully controlled component
function Toggle({ isOn, onChange }) {
function handleClick() {
onChange(!isOn);
}
}---
7. Passing Data Up to Parent
// BAD: Child fetches, passes up via Effect
function Parent() {
const [data, setData] = useState(null);
return <Child onFetched={setData} />;
}
function Child({ onFetched }) {
const data = useSomeAPI();
useEffect(() => {
if (data) onFetched(data);
}, [onFetched, data]);
}
// GOOD: Parent fetches, passes down
function Parent() {
const data = useSomeAPI();
return <Child data={data} />;
}Why: Data should flow down. Upward flow via Effects makes debugging hard.
---
8. Fetching Without Cleanup (Race Condition)
// BAD: No cleanup - race condition
function SearchResults({ query }) {
const [results, setResults] = useState([]);
useEffect(() => {
fetchResults(query).then(json => {
setResults(json); // "hello" response may arrive after "hell"
});
}, [query]);
}
// GOOD: Cleanup ignores stale responses
function SearchResults({ query }) {
const [results, setResults] = useState([]);
useEffect(() => {
let ignore = false;
fetchResults(query).then(json => {
if (!ignore) setResults(json);
});
return () => { ignore = true; };
}, [query]);
}---
9. App Initialization in Effect
// BAD: Runs twice in dev, may break auth
function App() {
useEffect(() => {
loadDataFromLocalStorage();
checkAuthToken(); // May invalidate token on second call!
}, []);
}
// GOOD: Module-level guard
let didInit = false;
function App() {
useEffect(() => {
if (!didInit) {
didInit = true;
loadDataFromLocalStorage();
checkAuthToken();
}
}, []);
}
// ALSO GOOD: Module-level execution
if (typeof window !== 'undefined') {
checkAuthToken();
loadDataFromLocalStorage();
}React useEffect Best Practices
A comprehensive guide teaching when to use useEffect in React, and more importantly, when NOT to use it. This skill is based on official React documentation and provides practical alternatives to common useEffect anti-patterns.
Purpose
Effects are an escape hatch from React's reactive paradigm. They let you synchronize with external systems like browser APIs, third-party widgets, or network requests. However, many developers overuse Effects for tasks that React handles better through other means.
This skill helps you:
- Identify when you truly need an Effect vs. when you don't
- Recognize common anti-patterns and their fixes
- Apply better alternatives like
useMemo,keyprop, and event handlers - Write Effects that are clean, maintainable, and free from race conditions
When to Use This Skill
Use this skill when you're:
- Writing or reviewing
useEffectcode - Using
useStateto store derived values - Implementing data fetching or subscriptions
- Synchronizing state between components
- Facing bugs with stale data or race conditions
- Wondering if your Effect is necessary
Trigger phrases:
- "Should I use useEffect for this?"
- "How do I fix this useEffect?"
- "My Effect is causing too many re-renders"
- "Data fetching with useEffect"
- "Reset state when props change"
- "Derived state from props"
How It Works
This skill provides guidance through three key resources:
1. Quick Reference Table - Fast lookup for common scenarios with DO/DON'T patterns 2. Decision Tree - Visual flowchart to determine the right approach 3. Detailed Anti-Patterns - 9 common mistakes with explanations and fixes 4. Better Alternatives - 8 proven patterns to replace unnecessary Effects
The skill teaches you to ask the right questions:
- Is there an external system involved?
- Am I responding to a user event or component appearance?
- Can this value be calculated during render?
- Do I need to reset state when a prop changes?
Key Features
1. Quick Reference Guide
Visual table showing the DO/DON'T for common scenarios:
- Derived state from props/state
- Expensive calculations
- Resetting state on prop change
- User event responses
- Notifying parent components
- Data fetching
2. Decision Tree
Clear flowchart that guides you from "Need to respond to something?" to the correct solution:
- User interaction → Event handler
- Component appeared → Effect (for external sync/analytics)
- Derived value needed → Calculate during render (+ useMemo if expensive)
- Reset state on prop change → Key prop
3. Anti-Pattern Recognition
Detailed examples of 9 common mistakes: 1. Redundant state for derived values 2. Filtering/transforming data in Effect 3. Resetting state on prop change 4. Event-specific logic in Effect 5. Chains of Effects 6. Notifying parent via Effect 7. Passing data up to parent 8. Fetching without cleanup (race conditions) 9. App initialization in Effect
Each anti-pattern includes:
- Bad example with explanation
- Good example with fix
- Why the anti-pattern is problematic
4. Better Alternatives
8 proven patterns to replace unnecessary Effects: 1. Calculate during render for derived state 2. useMemo for expensive calculations 3. key prop to reset state 4. Store ID instead of object for stable references 5. Event handlers for user actions 6. useSyncExternalStore for external stores 7. Lifting state up for shared state 8. Custom hooks for data fetching with cleanup
Usage Examples
Example 1: Derived State
Bad - Unnecessary Effect:
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
}Good - Calculate during render:
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
const fullName = firstName + ' ' + lastName; // Just compute it
}Example 2: Resetting State
Bad - Effect to reset:
function ProfilePage({ userId }) {
const [comment, setComment] = useState('');
useEffect(() => {
setComment('');
}, [userId]);
}Good - Key prop:
function ProfilePage({ userId }) {
return <Profile userId={userId} key={userId} />;
}
function Profile({ userId }) {
const [comment, setComment] = useState(''); // Resets automatically
}Example 3: Data Fetching with Cleanup
Bad - Race condition:
function SearchResults({ query }) {
const [results, setResults] = useState([]);
useEffect(() => {
fetchResults(query).then(json => {
setResults(json); // "hello" response may arrive after "hell"
});
}, [query]);
}Good - Cleanup flag:
function SearchResults({ query }) {
const [results, setResults] = useState([]);
useEffect(() => {
let ignore = false;
fetchResults(query).then(json => {
if (!ignore) setResults(json);
});
return () => { ignore = true; };
}, [query]);
}Example 4: Event Handler Instead of Effect
Bad - Effect watching state:
function ProductPage({ product, addToCart }) {
useEffect(() => {
if (product.isInCart) {
showNotification(`Added ${product.name}!`);
}
}, [product]);
function handleBuyClick() {
addToCart(product);
}
}Good - Handle in event:
function ProductPage({ product, addToCart }) {
function handleBuyClick() {
addToCart(product);
showNotification(`Added ${product.name}!`);
}
}When You DO Need Effects
Effects are appropriate for:
- Synchronizing with external systems - Browser APIs, third-party widgets, non-React code
- Subscriptions - WebSocket connections, global event listeners (prefer
useSyncExternalStore) - Analytics/logging - Code that needs to run because the component displayed
- Data fetching - With proper cleanup (or use your framework's built-in mechanism)
When You DON'T Need Effects
Avoid Effects for:
1. Transforming data for rendering - Calculate at the top level instead 2. Handling user events - Use event handlers where you know exactly what happened 3. Deriving state - Just compute it: const fullName = firstName + ' ' + lastName 4. Chaining state updates - Calculate all next state in the event handler 5. Notifying parent components - Call the callback in the same event handler 6. Resetting state - Use the key prop to create a fresh component instance
Best Practices
1. Start Without an Effect
Before adding an Effect, ask: "Is there an external system involved?" If no, you probably don't need an Effect.
2. Prefer Derived State
If you can calculate a value from props or state, don't store it in state with an Effect updating it.
3. Use the Right Tool
- Expensive calculation →
useMemo - User interaction → Event handler
- Reset on prop change →
keyprop - External subscription →
useSyncExternalStore - Shared state → Lift state up
4. Always Clean Up
If your Effect subscribes, fetches, or sets timers, return a cleanup function to prevent memory leaks and race conditions.
5. Avoid Effect Chains
Multiple Effects triggering each other causes unnecessary re-renders and makes code hard to follow. Calculate everything in one place (usually an event handler).
6. Test in Strict Mode
React 18+ Strict Mode mounts components twice in development to expose missing cleanup. If your Effect breaks, you need cleanup.
7. Consider Framework Solutions
For data fetching, prefer your framework's built-in solution (Next.js, Remix) or libraries (React Query, SWR) over manual Effects.
Reference Files
This skill includes three detailed reference documents:
1. SKILL.md - Quick reference table and decision tree 2. anti-patterns.md - 9 common mistakes with detailed explanations 3. alternatives.md - 8 better alternatives with code examples
Common Pitfalls
Multiple Re-renders
Symptom: Component re-renders many times in quick succession.
Cause: Effect that sets state based on state it depends on, creating a loop.
Fix: Calculate the final value in an event handler or during render.
Stale Data
Symptom: UI shows outdated values briefly before updating.
Cause: Using Effect to update derived state causes an extra render pass.
Fix: Calculate derived values during render instead of in state.
Race Conditions
Symptom: Fast typing shows results for old queries after new ones.
Cause: Missing cleanup in data fetching Effect.
Fix: Use cleanup flag (ignore variable) or AbortController.
Runs Twice in Development
Symptom: Effect runs twice on component mount in development.
Cause: React 18 Strict Mode intentionally mounts components twice to expose bugs.
Fix: Add proper cleanup. If it's app initialization that shouldn't run twice, use a module-level guard.
Resources
This skill is based on:
- React Official Docs: You Might Not Need an Effect
- React Official Docs: Synchronizing with Effects
- React Official Docs: Lifecycle of Reactive Effects
Summary
The golden rule: Effects are an escape hatch from React. If you're not synchronizing with an external system, you probably don't need an Effect.
Before writing useEffect, ask yourself: 1. Is this responding to a user interaction? → Use event handler 2. Is this a value I can calculate from props/state? → Calculate during render 3. Is this resetting state when a prop changes? → Use key prop 4. Is this synchronizing with an external system? → Use Effect with cleanup
Follow these patterns, and your React code will be more maintainable, performant, and bug-free.
Related skills
How it compares
Use react-useeffect for effect-to-derivation refactors; use React docs for initial hooks learning.
FAQ
When should I use useEffect instead of an event handler?
Use useEffect when synchronizing with an external system because a component appeared on screen, not in response to a specific user interaction.
How do I handle derived state without useEffect?
Calculate derived values directly during render, for example const fullName = firstName + ' ' + lastName, rather than storing them in state and syncing with an Effect.
How do I reset component state when a prop changes without useEffect?
Pass the changing prop as the key attribute on the component so React remounts it with fresh state automatically.
Is React Useeffect safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.