Skip to content

noctcore-react/no-effect-derived-state

An effect that only sets state derived from its dependencies is the “you might not need an effect” anti-pattern.

Recommended preset: error · Autofix: no · Suggestions: no · Type information: not needed

An effect whose only job is to setState a value purely derived from its own dependencies should not exist — the value should be computed during render (or with useMemo). The effect version runs an extra render pass, flashes stale UI for a frame, and is a common source of update loops. See the React docs, “You Might Not Need an Effect”.

Ships as error in the recommended preset. It is a heuristic, which is an argument for keeping it narrow rather than advisory: it fires only on an effect whose whole body is setX(...) of values read from its own deps, and anything with a branch, a call or a cleanup bails out unflagged.

This rule is deliberately conservative — it fires only on the unambiguous shape:

  • a useEffect / useLayoutEffect with a dependency array;
  • a non-async callback whose body is nothing but setX(...) statements;
  • every setter argument is a pure expression (no calls, awaits, new, JSX, or assignments) whose identifiers all resolve to a dependency;
  • at least one argument actually reads a dependency.
Incorrect
// derived state synced through an effect
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
Correct
// compute during render
const fullName = firstName + ' ' + lastName;

Anything with a branch, a side effect, an await, a cleanup, or an argument that reaches outside the dependency array bails out unflagged:

  • a setter argument that is a call (setX(compute(a))), which could be impure;
  • an effect with no dependency array, or an async callback;
  • a constant initialiser (setX(0)) that reads no dependency;
  • an if, a cleanup return, or any statement besides the setters.

If you rely on the extra render pass an effect gives you (rare, and usually a smell), disable it.