Skip to content

refactor(hooks): adopt eslint-plugin-react-hooks v6 rules (Next 16) - #115

Closed
catomean wants to merge 1 commit into
deps/next-16from
refactor/react-hooks-v6
Closed

refactor(hooks): adopt eslint-plugin-react-hooks v6 rules (Next 16)#115
catomean wants to merge 1 commit into
deps/next-16from
refactor/react-hooks-v6

Conversation

@catomean

Copy link
Copy Markdown
Collaborator

Follow-up to #114. Re-enables the three React-Compiler-era rules that #114 deferred — set-state-in-effect, error-boundaries, immutabilityall now ON — and resolves every one of the 30 violations with a real fix or a justified, documented exception. Stacks on #114 (base deps/next-16); merge order: #111#114 → this.

Real fixes (17 violations → genuine code improvements)

error-boundaries (12, one file)app/blog/[slug]/page.tsx wrapped its entire render in a try/catch that only re-threw. Removed it: notFound() and render errors now propagate to Next's not-found.tsx / error.tsx as intended. (The catch also spuriously logged notFound() as an error.)

immutability (5, auth pages) — the auth pages assigned window.location.href during render. Moved each redirect into a useEffect (placed above the early returns to satisfy rules-of-hooks), which keeps render pure while preserving the full-page-navigation behavior. Files: signin, signup, forgot-password, verify-email (×2).

Justified exceptions (13 set-state-in-effect)

Every one is a legitimate effect use — the rule is a heuristic that false-positives on them:

  • External-system sync: Supabase auth init (lib/auth), conversation/dashboard API loads (useDocumentChat, useDashboardStats, ConversationList), recovery-session validation (reset-password)
  • Browser-only hydration: localStorage (useLocalStorage), client-mount detection (Comments), client-only image-URL resolution that reads window.location (MDXComponents)
  • Seeding editable state from async-loaded data: settings form fields, governance advisory distribution, on-mount welcome messages

Forcing these off-effect would add complexity or risk behavior changes to auth/data code, so each carries an eslint-disable-next-line with a specific reason. The rule stays ON so genuinely-avoidable derived-state-in-effect fails CI in future code — strictly better than leaving it off.

Verification

  • eslint .0 problems (all three rules enabled)
  • prettier --check
  • tsc --noEmit
  • next build (Turbopack, all pages)
  • jest — 227 passed

🤖 Generated with Claude Code

Re-enables the three React-Compiler-era rules deferred in #114
(set-state-in-effect, error-boundaries, immutability) — all now ON — and
resolves every violation with a real fix or a justified, documented exception.

error-boundaries (12 → 0, real fix):
- app/blog/[slug]/page.tsx wrapped its whole render in try/catch that only
  re-threw. Removed it: notFound()/render errors now propagate to Next's
  not-found.tsx / error.tsx as intended (the catch also mislogged notFound()).

immutability (5 → 0, real fix):
- The auth pages assigned window.location.href during render. Moved each
  redirect into a useEffect (above the early returns for rules-of-hooks),
  preserving the full-page navigation while keeping render pure.
  signin, signup, forgot-password, verify-email (×2).

set-state-in-effect (13 → 0, justified inline disables):
- Every case is a legitimate effect use — external-system sync (Supabase auth,
  API loads), browser-only hydration (localStorage, client-mount), or seeding
  editable state from async-loaded data. Forcing them off-effect would add
  complexity or risk behaviour changes to auth/data code, so each carries an
  eslint-disable-next-line with a specific reason. The rule stays ON to catch
  genuinely-avoidable derived-state-in-effect in future code.

Verified: eslint . (0 problems), prettier, tsc --noEmit, next build, jest 227 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@catomean

Copy link
Copy Markdown
Collaborator Author

Landed on main via integration merge 21c3243 (commit f3e71d5 is in main). Closing as merged-in-effect.

@catomean catomean closed this Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant