effect/no-adjust-state-on-prop-change

Disallow adjusting state in an effect when a prop changes.

Validation prompt

Use this to decide whether a fired diagnostic is real or a false positive.

Fires when a useEffect's deps array contains an upstream prop AND the effect synchronously calls a state setter whose arguments do NOT also derive from that prop — i.e. the effect resets/adjusts state purely because a prop changed. False positive: the effect actually kicks off async work (fetch, debounce, subscription) whose later callback sets the state — the rule already requires the setter call to be synchronous.

Fix prompt

Use this once validation confirms the diagnostic is real.

The adjustment reveals duplicated state. The fix is removing the duplication so no setter is needed — not moving the setter somewhere else. Ask: 'Why does this state need resetting? Because it duplicates something already knowable from props/state. How do I make it derive its own staleness?' Patterns: (A) Store context, derive validity. If the effect resets a boolean flag, store the discriminator instead of the flag: const [failedUrl, setFailedUrl] = useState(null); const hasFailed = failedUrl === currentUrl; — when currentUrl changes, hasFailed becomes false automatically. If the effect resets a collection (cursor stack, list, etc.), store which inputs produced it: const items = state.forId === id ? state.items : DEFAULT; — stale items are never returned. If the effect clears an override when fresh data arrives, store what the override was set for: const override = entry?.forProp === prop ? entry.value : null; — override evicts itself when prop updates. (B) Compute inline. If the effect sets state to a value computable from existing state/props, delete that state entirely and compute it: return active ? phase : 0; instead of setPhase(0). For transition/animation classes, derive from state comparisons: const cls = text !== displayedText ? 'is-exit' : isEntering ? 'is-enter-start' : '';. (C) For resetting ALL state in a subtree, use a key prop: <Child key={userId} />. Do NOT use the prevProps-in-state pattern (tracking a previous value to detect changes during render) — it preserves the duplicated state instead of eliminating it. See https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes