Repository navigation
VC-216: adopt react/globals, react/purity and react/use-memo - #897
Merged
Merged
Conversation
Re-enable three of the ten React Compiler rules oxlint 1.79 split out and fix their findings on main: - purity: read the clock or RNG once per mount through a lazy useState initializer (sidebar skeleton width, streak window, usage popover's initial askedAt, MessageDelivery's wait, two lab scratches). - purity: chat-plane's pendingSince first-seen stamp keeps a commented disable; the stamp is idempotent per id and moves with the ref-held ledger when react/refs is adopted. - use-memo: inline the lab model-access factories (model-tiers holds its client in state so Reset needs no unused memo dependency). - globals: test probe components keep capturing hook values and counting renders through a test-file override; the chat-performance scratch's render counter keeps a commented disable. The other seven compiler rules stay off for their own follow-ups.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Regression tests: a held -> durable handoff part-way through the wait still ends it 600ms from the hold, and a pending stamp that arrives after the tab mounted never holds the wait past 600ms after mount (the clamp). - MessageDelivery's comment states the late-stamp case precisely; the pendingSince comment describes behaviour only. - ticket-kickoff scratch holds its client in useState like model-tiers. - vite.config.ts names the follow-up (VC-784) and rewraps its comment.
This was referenced Oct 18, 2026
hussainph
added a commit
that referenced
this pull request
Oct 18, 2026
…ld-toggles-and-persists-the-sid VC-808: sidebar ⌘B accepts ⌘ only, so ⌃B moves the caret again (stacked on #897 — merge after it)
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.
First slice of VC-216: turn
react/globals,react/purityandreact/use-memoback on and fix what they find. The other seven rules (refs,set-state-in-effect,exhaustive-effect-dependencies,hooks,immutability,memo-dependencies,preserve-manual-memoization) stay off. They're tracked in VC-784, which lists current finding counts per rule (376 in total).The ticket's triage said 8 findings for these rules. On current main there are 31: 21
globals, 8purityand 2use-memo. 20 of theglobalsfindings are in test files.Findings and fixes
react/purity(8)components/ui/sidebar.tsxSidebarMenuSkeleton—Math.random()inuseMemo(…, [])useState(() => …): the width is still drawn once per mount, and now that's guaranteed rather than a memo hintcomponents/chat/empty/streak-visual.tsx—useMemo(() => Date.now(), [])useState(() => Date.now()): same "taken once per mount" rule as the existing commentcomponents/usage-limits/usage-limits-popover.tsx—useRef(Date.now())(called on every render, only the first value kept)useState, then used as the ref's initial value. The focus gate behaves the samecomponents/chat/chat-plane.tsxMessageDelivery—Date.now()in render to work out the remaining waituseState), andremainingis clamped to 600ms. When the stamp is there at mount (the normal path, including a held → durable handoff, which keeps its stamp), the tab still shows atsince + 600. Before, every re-render restarted the timer with a smaller delay; now it doesn't restart. One behaviour difference: a stamp that arrives after the tab mounted, while its label stays non-failure, ends the wait at 600ms after mount. That can be earlier thansince + 600, but it's never sooner than 600ms after mount. The old code ended it at aboutsince + 600. I found no production path that stamps a mounted tab late; the new test reaches it by amending a held message directly. Both cases are covered by new tests (below)components/chat/chat-plane.tsxpendingSince—Date.now()inuseMemoreact/refsfinding. A clean fix needs that ledger restructured, which is outside this slice, so this line moves with the refs adoptionlab/scratches/host-connection.tsx(×2) —Date.now()in the fake source'suseMemoopenedAtis read once throughuseStateand becomes a memo dependency (it never changes)lab/scratches/split-tabs.tsx—useRef(Date.now())then reading.currentin renderuseState(() => Date.now())react/use-memo(2)lab/scratches/ticket-kickoff.tsx—useMemo(labModelAccess, [])useState(labModelAccess), the same way as model-tierslab/scratches/model-tiers.tsx—useMemo(labModelAccess, [generation])react-hooks/exhaustive-depsflaggenerationas unused, so the client and its generation now sit in oneuseState, and Reset swaps in a fresh client. Behaviour is the same: Reset remounts the provider with a new clientreact/globals(21)function Probe() { handle = useThing(); return null; }capture harnesses andrenders += 1render counters. I added oneoverridesentry invite.config.tsthat turnsreact/globalsoff for**/*.test.ts(x), with a comment explaining why. A probe exists to hand render-time values to its assertions; for render counters, the render count is the thing being measured. No test is compiled by React Compiler. Product code keeps the rule at error. I chose this over rewriting 20 working probes in 14 files that other PRs touch tonight. If you'd rather have those probes moved torenderHook, that's a mechanical follow-up.lab/scratches/chat-performance.tsx:CountedTurn'sturnRenders += 1is the scratch's measurement, and its doc comment already explains why it's a memo wrapper rather than a Profiler. It gets a commented disable on that one line.So there are two
oxlint-disable-next-linecomments, each covering one line, naming one rule and giving its reason. There are no blanket suppressions.Verification
vp lint: 0 warnings, 0 errors (it was 31 errors with the three rules on before these fixes).vp fmt --checkon the changed files: clean.vp run --filter @volli/desktop typecheckandvp exec tsc --noEmit -p tsconfig.json: clean.chat-plane.window.test.tsx:command.recordedat 300ms; the tab is absent at 599ms and shows at 600ms. This fails ifsinceis ignored.Math.min(600, …)clamp, where it would show at 1200ms.chat-plane.windowandchat-plane.progresspass (81 tests) after the follow-up commit. Earlier: 6 files and 120 tests passed —chat-plane.progress,chat-plane.window,chat-plane,chat-plane.acceptance(these cover the delivery footer and its delayed "Pending" tab),usage-limits-popoverandchat-empty-state(streak). None of the touched files are on a protected coverage list. The only fix that changes observable behaviour isMessageDelivery, and the two tests above cover it.Overlap with open PRs
chat-plane.tsxis also touched by #873 (VC-614, large) and #878 (VC-516). Their hunks are at ~174/253/335/1894/2979. Mine are at ~1056 (pendingSince) and ~3012 (MessageDelivery), a few lines each, so I expect clean merges.