Answer set-state-in-effect instead of downgrading it - #160
Merged
Conversation
`eslint-config-next@16` brought eslint-plugin-react-hooks 7, whose `set-state-in-effect` rule the Next 16 bump silenced repo-wide with a `"warn"` override. Drop the override and take the reports: - The rail's collapse state came out of a mount effect reading the attribute the pre-paint script leaves on <html>. It is a value that already exists at hydration, so `useSyncExternalStore` reads it, with a server snapshot keeping the first render matching the prerendered HTML. - The shared-filter-link reveal was a one-shot effect over a ref; it is now the render-phase adjustment React documents for exactly this. - The pill's "mounted yet?" and pointer-proximity effects, plus the `keydown` listener that was torn down and re-subscribed on every keystroke, are now `useIsClient`, `useEventListener` and `useHotkeys`. What the listener's ladder of early returns said by falling through, `enabled` and `ignoreEventWhen` now say out loud. - `useNearbyExamples` keeps its measure-then-render pass — the one thing `useLayoutEffect` is for, and the one shape the rule cannot tell apart from a cascading render — behind a single scoped disable. Folding the no-IntersectionObserver fallback into the same unbounded seed leaves only that one `setNearby` to exempt. - `hooks/use-mobile.ts` is shadcn's, kept stock, so the rule is switched off for that file alone rather than for the app. Also fixes the two `exhaustive-deps` warnings the override left visible, and the Tailwind classes with a canonical form (`duration-1078`, `ms-(--nav-offset)`, `**:data-[slot=…]`) — same CSS, verified against the built stylesheet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y9J9K5TobdJo49EU4Z1Fp4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
eslint-config-next@16brought eslint-plugin-react-hooks 7, and the Next 16 bump silenced its newset-state-in-effectrule app-wide with a"warn"override, with a comment promising a later refactor. This is that refactor: the override is gone and every report is answered.What changed in
Nav.tsxdata-nav-collapsedoff<html>useSyncExternalStore, with a server snapshot so the first render matches the prerendered HTML — the attribute already exists at hydration, it was never something to sync touseEffect(() => setReady(true), [])(×2)useIsClient()mousemoveeffect that also resetnearwhen the rail openeduseEventListener, with the reset folded into the handlerkeydownlistener with a ladder of early returns, re-subscribed on every keystrokeuseHotkeys, whereenabled/ignoreEventWhensay out loud what the ladder said by falling throughuseNearbyExampleskeeps its measure-then-render pass behind a single scopedeslint-disable: that is the one thinguseLayoutEffectexists for, and the one shape the rule cannot tell apart from a cascading render. Folding the no-IntersectionObserverfallback into the same unbounded seed means only onesetNearbyneeds the exemption.hooks/use-mobile.tsis shadcn's and stays stock, so the rule is switched off for that file alone — it cannot simply be deleted from the config, since the rule is an error by default in the plugin's recommended set.Also
react-hooks/exhaustive-depswarnings the override left standing (setSearchmissing fromdismissSearchand from the keydown effect — the latter now moot).duration-[1078ms]→duration-1078,md:[margin-inline-start:var(--nav-offset)]→md:ms-(--nav-offset),[&_[data-slot=…]]:→**:data-[slot=…]:,[&>[data-slot=…]]:→*:data-[slot=…]:. Byte-identical declarations and equal specificity, checked against the built stylesheet before and after.New deps:
usehooks-ts,react-hotkeys-hook.Verified
pnpm lintclean (no warnings),tsc --noEmitclean,next build(static export) green. In the browser, on both the export and the dev server, with no hydration warnings: collapse preference survives a reload with no flash; a?q=link opens a rail that was stored collapsed without overwriting that preference; Escape clears the filter and the rail stays open; type-to-search, Backspace,/and ⌘K all behave as before.🤖 Generated with Claude Code
https://claude.ai/code/session_01Y9J9K5TobdJo49EU4Z1Fp4