LLM reference: Fetch llms.txt for the React documentation index and latest API. Key page for this skill: You Might Not Need an Effect.
useEffect — naming, scope, and when to skipEffects are an escape hatch from React. They let you synchronize with external systems. If there is no external system involved, you usually should not use an Effect.
Name every useEffect (strong recommendation)
Always pass a named function expression to useEffect, not an anonymous arrow. Treat this as the default style for new and edited code.
// Avoid for useEffect (anonymous — hard to skim and debug)
useEffect(() => {
document.title = `${count} items`
}, [count])
// Prefer — intent at the call site; named stacks in errors and DevTools
useEffect(
function updateDocumentTitle() {
document.title = ${count} items
},
[count],
)
Equivalent at a glance: `useEffect(function connectToInventoryWebSocket() { ... }, [warehouseId])` vs `useEffect(() => { ... }, [warehouseId])`.
**Why this matters**
- **Reviews and navigation**: You can skim effect names without reading bodies.
- **Errors and profiling**: Stack traces show `at connectToInventoryWebSocket` instead of `at (anonymous)`; React DevTools shows the same.
**Cleanup**: When teardown is non-trivial, prefer a named cleanup for symmetry and stack clarity:
```tsx
useEffect(function pollServerForUpdates() {
const intervalId = setInterval(/* ... */, 5000);
return function stopPollingServer() {
clearInterval(intervalId);
};
}, [serverId]);
Other hooks: The same readability applies wherever you pass a function you did not name — useCallback, useMemo factories, reducers — but useEffect benefits most because timing, dependencies, and cleanup are the hardest to infer.
Custom hooks: Still name effects inside the hook; encapsulation does not replace named effects when a hook contains several.
Indicators from naming (what to do next)
Naming is a design review at the keyboard. Use it together with Anti-Patterns (examples and fixes live there — avoid duplicating that material here).
| Signal | Meaning | Where to look |
|---|---|---|
| Name needs “and” / “also” | Unrelated concerns in one effect | Split into separate effects, each with one name |
Name like syncDerivedValue, updateStateFromState, setXBasedOnY |
Likely derived state or state-to-state sync | Anti-Patterns §1 — derive during render; useMemo if expensive |
Name like notifyParentOfChange, reportStateToParent |
Parent updates driven by child state | Anti-Patterns §6–7 — event handler, lift data, or controlled pattern |
Name like resetFormOnSubmitFlag |
User intent expressed via state hop | Anti-Patterns §4 — handle in the event handler |
Clear, external verbs: connectTo…, subscribeTo…, initialize…, synchronize… (with browser, network, map SDK, etc.) |
Often legitimate Effect territory | Still name them; add cleanup when needed |
If the honest name sounds like internal React bookkeeping, the code often belongs in render, an event handler, or a different pattern — see the linked sections.
Quick reference
| Situation | DON'T | DO |
|---|---|---|
| Derived state from props/state | useState + useEffect |
Calculate during render |
| Expensive calculations | useEffect to cache |
Compute in render; useMemo only if needed (often unnecessary with React Compiler) |
| 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 (
useSyncExternalStorewhen it fits) - Analytics/logging tied to display
- Data fetching with proper cleanup (or the framework’s mechanism)
Strict Mode (dev): Effects run setup twice on mount to surface missing cleanup. Add teardown for subscriptions, timers, and fetches — see Anti-Patterns §8–9.
React Compiler and ESLint
- Render work: Prefer deriving values during render. With React Compiler enabled, skip manual
useMemounless profiling shows a need or Compiler is off. - Effects: Compiler does not replace Effects for external synchronization. Changed memoization can change when effects re-run — fix effect design (deps, splits,
useEffectEvent) rather than disabling the compiler. - Lint: Use
eslint-plugin-react-hooks@latest(recommendedpreset). Rules such asset-state-in-effectalign with Anti-Patterns. Remove standaloneeslint-plugin-react-compilerif present.
Legitimate effects: useEffectEvent
When setup must stay subscribed (connection, interval, listener) but part of the callback should read latest props/state without re-running setup, extract non-reactive logic with useEffectEvent:
- Name like user-visible events:
onConnected,onTick— notonMount/onUpdate. - Call only from Effects (or other Effect Events in the same component); never during render, in event handlers, or as props to children.
- Omit from the Effect dependency array (enforced by lint).
Do not use useEffectEvent to hide dependencies that should re-trigger the Effect.
Example and patterns: Alternatives §9. Docs: Separating Events from Effects, useEffectEvent.
When you DON’T need Effects (pointer)
- Transforming data for rendering — compute during render
- User events — event handlers
- Deriving state — compute it (
const x = f(a, b)) - Chaining updates — do it in one event handler where possible
Details and fixes: Anti-Patterns. Alternatives: Better Alternatives.
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? useMemo only if needed (Compiler often makes this unnecessary)
└── Need to reset state when prop changes?
└── Use KEY PROP on component
References
- React — You Might Not Need an Effect
- React — Separating Events from Effects ·
useEffectEvent - React — React Compiler · Compiler 1.0 blog
- Neciu Dan — Start naming your useEffect functions (naming discipline, splitting when names resist, overlap with “you might not need an effect”)
- Kyle Shevlin — useEncapsulation (custom hooks; still name effects inside hooks)