noctcore-react/require-effect-cancellation
A
setStateafter anawait/.theninside an effect needs a cancellation guard.
Recommended preset: error · Autofix: no · Suggestions: no · Type information: not needed
A state update that runs after an async step inside an effect, with nothing to cancel it, is the
classic “setState after unmount / stale response” bug. If the effect re-runs (its deps change) or
the component unmounts before the promise settles, the update lands on a dead effect — and a slow
response can overwrite a newer one. Guard it with an AbortController, a cancelled/isMounted
flag, or a cleanup return.
Severity
Section titled “Severity”Ships as error in the recommended preset. It is a heuristic and not autofixable, which is an
argument for making it precise rather than for making it advisory: a preset that ships warn
gives a consumer a severity they cannot act on, and a project that forbids warn outright
cannot use the preset at all. A state update after an await with nothing to cancel it is a real
bug, and any recognisable guard (an AbortController, a cancel flag, a cleanup) silences it.
What it flags
Section titled “What it flags”An await (or a .then(cb)) inside a useEffect / useLayoutEffect callback, followed by a
setState/dispatch, when the effect has no recognisable cancellation guard:
// nothing cancels the updateuseEffect(() => { async function load() { const data = await fetchThing(id); setData(data); } load();}, [id]);// guarded with a cancelled flaguseEffect(() => { let cancelled = false; fetchThing(id).then((data) => { if (!cancelled) setData(data); }); return () => { cancelled = true; };}, [id]);What it does not flag
Section titled “What it does not flag”The rule is intentionally conservative: any recognisable guard — an AbortController/AbortSignal,
a cancelled/isMounted-style boolean flag that is read, an if test on such a flag, or a cleanup
return — silences it, so a real finding is almost always genuine. It also leaves alone:
- a synchronous
setStatein an effect, with no async step; - an
awaitor.thenwith no state update after it.
When not to use it
Section titled “When not to use it”If your effects never touch component state after an async step, or you manage cancellation through a pattern this rule cannot see, disable it.