From skills
Guides proper React useEffect usage, covering when to use Effects, how to avoid unnecessary Effects, and solutions for common anti-patterns like derived state, effect chains, and event-specific logic.
How this skill is triggered — by the user, by Claude, or both
Slash command
/skills:react-effect-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
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.
Why does this code run?
// ❌ Bad
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(firstName + " " + lastName);
}, [firstName, lastName]);
// ✅ Good - calculate during render
const fullName = firstName + " " + lastName;
// ❌ 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.
// ❌ Bad
useEffect(() => {
setComment("");
}, [userId]);
// ✅ Good - use key to reset
<Profile userId={userId} key={userId} />;
// ❌ Bad
useEffect(() => {
setSelection(null);
}, [items]);
// ✅ Good - derive from state
const selection = items.find((item) => item.id === selectedId) ?? null;
// ❌ Bad
useEffect(() => {
if (product.isInCart) {
showNotification("Added " + product.name + "!");
}
}, [product]);
// ✅ Good - in event handler
function handleBuyClick() {
addToCart(product);
showNotification("Added " + product.name + "!");
}
// ❌ Bad
useEffect(() => {
if (jsonToSubmit !== null) {
post("/api/register", jsonToSubmit);
}
}, [jsonToSubmit]);
// ✅ Good - in event handler
function handleSubmit(e) {
e.preventDefault();
post("/api/register", { firstName, lastName });
}
// ❌ 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);
}
}
}
// ❌ 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();
}
}, []);
}
// ❌ 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);
}
}
// ❌ 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} />;
}
// ❌ 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,
);
// ✅ Correct - cleanup ignores stale responses
useEffect(() => {
let ignore = false;
fetchResults(query).then((json) => {
if (!ignore) setResults(json);
});
return () => {
ignore = true;
};
}, [query]);
| 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 |
npx claudepluginhub nbbaier/agent-skillsGuides React developers through a decision tree to verify useEffect necessity before writing, suggesting alternatives like inline derivation, event handlers, key prop, or TanStack Query to prevent anti-patterns.
Provides expert guidance on React hooks, composition patterns, and performance optimization. Automatically activates when discussing hooks, state management, re-renders, or component composition.
Audits React components for unnecessary useEffect patterns, detecting 9 anti-patterns from 'You Might Not Need an Effect' with severity levels and fix proposals.