[Frontend] Adopt the React Compiler lint rules from eslint-plugin-react-hooks v7 #182
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
#21 wired up the real
eslint-plugin-react-hooks(v7.1.1) and enabled exactly the two rules that issue asked for:It deliberately did not spread the plugin's
recommendedpreset. In v7 that preset no longer means "the two classic hooks rules" — it enables 16, adding 14 React Compiler rules, mostly aterror:purity,set-state-in-render,set-state-in-effect,immutability,refs,globalsstatic-components,use-memo,preserve-manual-memoizationerror-boundaries,incompatible-library,unsupported-syntax,config,gating(
recommended-latestis 17 — it also addsvoid-use-memo.)These exist so the React Compiler can safely auto-memoize components: it must be able to prove the code follows the Rules of React. Adopting them is a step toward enabling the compiler, not general lint hygiene — which is why it was scoped out of #21 rather than smuggled in.
The rationale is recorded in
webapp/frontend/eslint.config.js.Actual scope (measured, not estimated)
Ran the full v7
recommendedpreset againstmain(4328226, i.e. after #21 and #103) as a throwaway probe config. It is much smaller than expected — 12 findings, all errors:By file:
The other 12 compiler rules report zero violations — the codebase is already broadly compliant.
Why this is still non-trivial despite being only 12
set-state-in-effectis the bulk of it, and it is rarely a one-line fix. CallingsetStateinside an effect usually signals state that should be derived during render, lifted, or keyed instead — so each site needs a judgement call about the intended data flow, and a careless "fix" can introduce a render loop or change behaviour. Expect real review per site, not a mechanical pass.preserve-manual-memoization(1 site) flags auseMemo/useCallbackwhose memoization the compiler couldn't preserve.Proposed approach
eslint.config.jsfrom the two explicit rules to spreadingreactHooks.configs.recommended(orrecommended-latest), and drop the explanatory comment.exhaustive-depsatwarnand the rest at their preset severities.Acceptance criteria
eslint-disable).eslint.config.jsuses the plugin'srecommendedpreset rather than listing rules explicitly.npm run lintclean (0 errors, 0 warnings).set-state-in-effectfix does change behaviour.Reproducing the measurement
The probe config used (not committed):
Filed as a follow-up to #21 (v3.5.0 — Frontend Platform & PWA).
Picking this up alongside #464 as a v4.4.0 engineering lane. The rules should be on before the rebuild writes new components, so that new code is compliant from the start rather than swept afterwards. The July measurement (12 sites) predates v4.3.0's changes to most of the named files, so the first step is to re-run the probe config and work from the current count. Each site gets a deliberate fix, per the body; the preset flip comes last.
Done; pull request to follow this comment (auto-merge on green CI).
The re-measurement mattered: 40 findings across 22 files, not the 12 from July. Every one is fixed by restructuring, none suppressed. The dominant pattern was a
loadingorstaleflag an effect raised on its way past, now a value derived during render from what the data in hand was fetched for. The rule also turned up two genuine hazards beyond hygiene:ElapsedTimerread the clock during render (a re-render would move its origin), and the Shelf'sonPointerUpremoved itself by a binding that was not yet initialised, so the listener it detached was whichever the closure captured; both are rewritten.Config:
recommended-latest(a strict superset ofrecommendedin v7),exhaustive-depskept atwarn, comment rewritten. The React Compiler itself stays a separate decision, as the body says.One user-visible improvement rode along with its own changelog entry (the stat-block autosave indicator now keeps "Saved"); its twin on the body autosave is #539 for the rebuild's wiki lane. Two follow-ups filed from the phase 0 lanes overall: #536 and #539.