react-effects
Guidelines for when to use (and avoid) useEffect in React components
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
React Effects Guidelines
Primary reference: https://react.dev/learn/you-might-not-need-an-effect
Quick Decision Tree
Before adding useEffect, ask:
- Can I calculate this during render? → Derive it, don't store + sync
-
Is this resetting state when a prop changes? → Use
keyprop instead - Is this triggered by a user event? → Put it in the event handler
- Am I syncing with an external system? → Effect is appropriate
Legitimate Effect Uses
- DOM manipulation (focus, scroll, measure)
- External widget lifecycle (terminal, charts, non-React libraries)
- Browser API subscriptions (ResizeObserver, IntersectionObserver)
- Data fetching on mount/prop change
- Global event listeners
Common Anti-Patterns
// ❌ Derived state stored separately
const [fullName, setFullName] = useState('');
useEffect(() => setFullName(first + ' ' + last), [first, last]);
// ✅ Calculate during render
const fullName = first + ' ' + last;
// ❌ Event logic in effect
useEffect(() => { if (isOpen) doSomething(); }, [isOpen]);
// ✅ In the handler
const handleOpen = () => { setIsOpen(true); doSomething(); };
// ❌ Reset state on prop change
useEffect(() => { setComment(''); }, [userId]);
// ✅ Use key to reset
<Profile userId={userId} key={userId} />
External Store Subscriptions
For subscribing to external data stores (not DOM APIs), prefer useSyncExternalStore:
// ❌ Manual subscription in effect
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
const update = () => setIsOnline(navigator.onLine);
window.addEventListener('online', update);
window.addEventListener('offline', update);
return () => { /* cleanup */ };
}, []);
// ✅ Built-in hook for external stores
const isOnline = useSyncExternalStore(
subscribe,
() => navigator.onLine, // client
() => true // server
);
Data Fetching Cleanup
Always handle race conditions with an ignore flag:
useEffect(() => {
let ignore = false;
fetchData(query).then(result => {
if (!ignore) setData(result);
});
return () => { ignore = true; };
}, [query]);
App Initialization
For once-per-app-load logic (not once-per-mount), use a module-level guard:
let didInit = false;
function App() {
useEffect(() => {
if (!didInit) {
didInit = true;
loadDataFromLocalStorage();
checkAuthToken();
}
}, []);
}
Or run during module initialization (before render):
if (typeof window !== 'undefined') {
checkAuthToken();
loadDataFromLocalStorage();
}
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-01-31
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!