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):
react-hooks/immutability (1 site) — easiest, one fix.
react-hooks/refs (~5 sites) — refs-during-render are usually structural fixes (move the ref read into an effect / event handler).
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.
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
Out of scope
Adopting the React Compiler itself. These rules are a prerequisite for that work, but adoption is a separate decision.
Parent: #884 (Next.js 16 Upgrade Plan)
Context
PR #939 / commit
42b73de1bumpedeslint-plugin-react-hooksfrom v5 → v7 as part of the ESLint 9 cascade. v7 ships five new React-Compiler-aware checks via itsrecommendedpreset — 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.mjswith a note pointing to this ticket:Rules + approximate violation counts at landing
react-hooks/set-state-in-effectsetStatecalled synchronously inside auseEffectbody — causes cascading rendersreact-hooks/refsreact-hooks/incompatible-libraryuseReactTable()returns functions that aren't memoisable safely — affects React Compiler optimisationreact-hooks/immutabilityreact-hooks/static-componentsReference article on
set-state-in-effect: https://medium.com/@maroobsyed/error-calling-setstate-synchronously-within-an-effect-can-trigger-cascading-renders-7e6fb9d971b2Suggested approach
Tackle one rule at a time, ideally in this order (lowest blast radius → highest):
react-hooks/immutability(1 site) — easiest, one fix.react-hooks/refs(~5 sites) — refs-during-render are usually structural fixes (move the ref read into an effect / event handler).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.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:
"off"override ineslint.config.mjs.Acceptance
react-hooks/*rules disabled ineslint.config.mjsare either:npm run lintclean.Out of scope
Adopting the React Compiler itself. These rules are a prerequisite for that work, but adoption is a separate decision.