react-effect-patterns
React Effect Patterns
Effects are an escape hatch from React for synchronizing with external systems. Removing unnecessary Effects makes code easier to follow, faster to run, and less error-prone.
When to Use Effects
- Synchronizing with external systems (non-React widgets, network, browser DOM)
- Analytics on component display
- Data fetching (with cleanup for race conditions)
When NOT to Use Effects
- Transforming data for rendering
- Handling user events
- Updating state based on other state
Decision Tree
Why does this code run?
- Component displayed → Effect
- User action → Event handler
- Calculable from props/state → Calculate during render
Anti-Patterns and Solutions
1. Derived State
// ❌ Bad
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(firstName + " " + lastName);
}, [firstName, lastName]);
// ✅ Good - calculate during render
const fullName = firstName + " " + lastName;
2. Expensive Calculations
// ❌ Bad
const [visibleTodos, setVisibleTodos] = useState([]);
useEffect(() => {
setVisibleTodos(getFilteredTodos(todos, filter));
}, [todos, filter]);
// ✅ Good - useMemo for expensive operations
const visibleTodos = useMemo(
() => getFilteredTodos(todos, filter),
[todos, filter],
);
Use console.time()/console.timeEnd() to measure. Memoize if ≥1ms.
3. Reset State on Prop Change
// ❌ Bad
useEffect(() => {
setComment("");
}, [userId]);
// ✅ Good - use key to reset
<Profile userId={userId} key={userId} />;
4. Adjust Part of State on Prop Change
// ❌ Bad
useEffect(() => {
setSelection(null);
}, [items]);
// ✅ Good - derive from state
const selection = items.find((item) => item.id === selectedId) ?? null;
5. Event-Specific Logic
// ❌ Bad
useEffect(() => {
if (product.isInCart) {
showNotification("Added " + product.name + "!");
}
}, [product]);
// ✅ Good - in event handler
function handleBuyClick() {
addToCart(product);
showNotification("Added " + product.name + "!");
}
6. Form Submission
// ❌ Bad
useEffect(() => {
if (jsonToSubmit !== null) {
post("/api/register", jsonToSubmit);
}
}, [jsonToSubmit]);
// ✅ Good - in event handler
function handleSubmit(e) {
e.preventDefault();
post("/api/register", { firstName, lastName });
}
7. Effect Chains
// ❌ Bad - cascading Effects
useEffect(() => {
setGoldCardCount((c) => c + 1);
}, [card]);
useEffect(() => {
setRound((r) => r + 1);
}, [goldCardCount]);
useEffect(() => {
setIsGameOver(true);
}, [round]);
// ✅ Good - calculate + update in handler
const isGameOver = round > 5;
function handlePlaceCard(nextCard) {
setCard(nextCard);
if (nextCard.gold) {
if (goldCardCount < 3) {
setGoldCardCount(goldCardCount + 1);
} else {
setGoldCardCount(0);
setRound(round + 1);
}
}
}
8. App Initialization
// ❌ Bad - runs twice in dev
useEffect(() => {
loadDataFromLocalStorage();
checkAuthToken();
}, []);
// ✅ Good - module level or guard
let didInit = false;
function App() {
useEffect(() => {
if (!didInit) {
didInit = true;
loadDataFromLocalStorage();
checkAuthToken();
}
}, []);
}
9. Notify Parent of State Changes
// ❌ Bad - extra render pass
useEffect(() => {
onChange(isOn);
}, [isOn, onChange]);
// ✅ Good - update both in handler
function updateToggle(nextIsOn) {
setIsOn(nextIsOn);
onChange(nextIsOn);
}
// ✅ Also good - lift state (controlled component)
function Toggle({ isOn, onChange }) {
function handleClick() {
onChange(!isOn);
}
}
10. Pass Data to Parent
// ❌ Bad - child fetches, passes up
useEffect(() => {
if (data) onFetched(data);
}, [data]);
// ✅ Good - parent fetches, passes down
function Parent() {
const data = useSomeAPI();
return <Child data={data} />;
}
11. External Store Subscription
// ❌ Bad - manual subscription
useEffect(() => {
const handler = () => setIsOnline(navigator.onLine);
window.addEventListener("online", handler);
window.addEventListener("offline", handler);
return () => {
window.removeEventListener("online", handler);
window.removeEventListener("offline", handler);
};
}, []);
// ✅ Good - useSyncExternalStore
return useSyncExternalStore(
subscribe,
() => navigator.onLine,
() => true,
);
12. Data Fetching with Race Conditions
// ✅ Correct - cleanup ignores stale responses
useEffect(() => {
let ignore = false;
fetchResults(query).then((json) => {
if (!ignore) setResults(json);
});
return () => {
ignore = true;
};
}, [query]);
Quick Reference
| Scenario | Solution |
|---|---|
| Transform data | Calculate during render |
| Expensive calculation | useMemo |
| Reset all state on prop | key attribute |
| Adjust state on prop | Derive during render |
| Share event logic | Extract function, call from handlers |
| User events | Event handlers |
| External system sync | Effect |
| Notify parent | Update in handler or lift state |
| Init once | Module-level or guard variable |
| External store | useSyncExternalStore |
| Fetch data | Effect with cleanup |
More from nbbaier/agent-skills
ideation
Guides structured ideation through Socratic questioning to explore problems, opportunities, and solutions. Use when brainstorming features, exploring use cases, or thinking through new ideas.
51implementation-guide
Generate comprehensive implementation guides for coding tasks instead of writing code directly. Use when the user requests detailed implementation documentation, step-by-step development guides, or when they want to implement features themselves using tools like Cursor. Creates exhaustive guides with background context, architecture decisions, milestones with verification points, and rationale for a "build-it-yourself" workflow.
13val-town-cli
Manage Val Town projects using the vt CLI. Use when working with Vals (Val Town serverless functions), syncing code to Val Town, creating HTTP endpoints, streaming logs, or managing Val Town branches. Triggers on tasks involving Val Town development, val creation/editing, or when user mentions "vt", "val town", or "vals".
11logging-best-practices
Use before implementing logs in a medium to large scale production system.
11html-tools
Build single-file HTML tools — self-contained HTML+JS+CSS applications that solve a specific problem without a build step. Use this skill whenever the user asks to build a utility, converter, viewer, debugger, or any small interactive web tool. Also trigger when the user says "build me a tool that...", "make a quick app for...", "I need a single-file HTML page that...", or wants to create something that could be hosted as a static file. This skill is specifically for practical, utility-focused tools (not landing pages, portfolios, or marketing sites — use frontend-design for those).
5logging best practices
Use before implementing logs in a medium to large scale production system.
1