Skip to content

[P2] React-hooks v7 rules: address anti-patterns surfaced by the recommended preset #941

@frano-m

Description

@frano-m

Parent: #884 (Next.js 16 Upgrade Plan)

Context

PR #939 / commit 42b73de1 bumped eslint-plugin-react-hooks from v5 → v7 as part of the ESLint 9 cascade. v7 ships five new React-Compiler-aware checks via its recommended preset — all of which surface real anti-patterns in the current codebase (~20 violations across ~10 files).

To keep #939 scoped to tooling, those rules were turned off in eslint.config.mjs with a note pointing to this ticket:

// The remaining react-hooks v7 rules below are React-Compiler-aware
// checks that surface real anti-patterns in the codebase. Disabled here
// to keep this PR scoped to the tooling upgrade; revisit alongside any
// future React Compiler adoption.
"react-hooks/immutability": "off",
"react-hooks/incompatible-library": "off",
"react-hooks/refs": "off",
"react-hooks/set-state-in-effect": "off",
"react-hooks/static-components": "off",

Rules + approximate violation counts at landing

Rule Count What it catches
react-hooks/set-state-in-effect ~14 errors setState called synchronously inside a useEffect body — causes cascading renders
react-hooks/refs ~5 errors Refs accessed during render
react-hooks/incompatible-library ~4 warnings TanStack Table's useReactTable() returns functions that aren't memoisable safely — affects React Compiler optimisation
react-hooks/immutability 1 error A value being mutated that should be immutable
react-hooks/static-components (warn, not enabled) Component identity must be stable across renders

Reference article on set-state-in-effect: https://medium.com/@maroobsyed/error-calling-setstate-synchronously-within-an-effect-can-trigger-cascading-renders-7e6fb9d971b2

Suggested approach

Tackle one rule at a time, ideally in this order (lowest blast radius → highest):

  1. react-hooks/immutability (1 site) — easiest, one fix.
  2. react-hooks/refs (~5 sites) — refs-during-render are usually structural fixes (move the ref read into an effect / event handler).
  3. react-hooks/incompatible-library (~4 warnings, TanStack Table call sites) — likely needs per-site suppression with a justification comment, since the library itself isn't compatible. Or migrate to a memoisable equivalent if one exists.
  4. react-hooks/set-state-in-effect (~14 sites) — biggest cluster. Each effect needs case-by-case evaluation: derive instead of effect, functional updater, useReducer, or extract effect.

For each cluster, the PR pattern would be:

  • Remove the corresponding "off" override in eslint.config.mjs.
  • Fix or per-site-suppress each violation.
  • Land as a focused PR.

Acceptance

  • All five react-hooks/* rules disabled in eslint.config.mjs are either:
    • enabled and the underlying violations fixed, or
    • per-site suppressed with a justification comment that explains why (not just "TODO fix").
  • npm run lint clean.
  • No runtime regressions for affected components.

Out of scope

Adopting the React Compiler itself. These rules are a prerequisite for that work, but adoption is a separate decision.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions