Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
e26a7ef to
e02a323
Compare
e02a323 to
6858ff4
Compare
6858ff4 to
8d89540
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8d89540. Configure here.
Co-authored-by: Cursor <cursoragent@cursor.com>
8d89540 to
d7e6e5f
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
5 tasks
aidenybai
added a commit
that referenced
this pull request
May 8, 2026
…167) Cleanup pass over the recently-merged state-and-effects rules from #153–#157, #162 surfacing six audit findings. 1. **De-duplicate constant sets.** Four overlapping allowlists (`SUBSCRIPTION_METHOD_NAMES` ⊂ `EXTERNAL_SYNC_MEMBER_METHOD_NAMES`, `FETCH_MEMBER_OBJECTS` ⊂ `EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS`, `FETCH_CALLEE_NAMES` ⊂ `EXTERNAL_SYNC_DIRECT_CALLEE_NAMES` & `EVENT_TRIGGERED_SIDE_EFFECT_CALLEES`, and the timer/scheduler list shared between `prefer-use-effect-event` and `no-effect-chain`) now spread from a single base set so adding a new HTTP client / subscribe verb in one place propagates everywhere. 2. **Unify `getRootIdentifierName`.** `server.ts` had a private copy that walked through CallExpression chains; the shared helper in `helpers.ts` did not. Added an opt-in `followCallChains` option and deleted the duplicate. 3. **Remove dead branch in `isExternalSyncEffect`.** A redundant `else if (effectCallback.body?.type !== "BlockStatement")` whose body was a comment-only no-op. 4. **Migrate `noDerivedUseState` to the shared root walker.** Drops the inline MemberExpression chain walk and uses `getRootIdentifierName` for consistency. 5. **Drop `noEffectEventHandler`'s deference to `noEventTriggerState`.** The deference predicate only checked two of the five preconditions `noEventTriggerState` actually requires (single dep, handler-only writes, not render-reachable were not verified), so when those failed the narrower rule didn't fire AND this rule deferred — a silent drop. Both rules now fire independently; the two messages frame the same code differently and a duplicate diagnostic is strictly better than a missing one. Also removes ~60 lines of useState-name-tracking machinery that only existed to support the deference. Existing test was updated and a new regression pinning the double-warn was added. 6. **Receiver-gate `push` / `replace`.** Both names live in `EVENT_TRIGGERED_SIDE_EFFECT_MEMBER_METHODS` but also denote `Array.prototype.push` and `String.prototype.replace`. Moved them to a separate `EVENT_TRIGGERED_NAVIGATION_METHOD_NAMES` set that's only counted when the receiver root is in `NAVIGATION_RECEIVER_NAMES` (`router`, `navigation`, `navigator`, `history`, `location`). Two regressions added — `arr.push(x)` does not fire, `router.push(path)` still does. All checks green: 617/617 tests, typecheck, lint, format. Co-authored-by: Cursor <cursoragent@cursor.com>
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.

New rule (severity:
warn) inspired by Vercel Labs'advanced-use-latestand React's Separating Events from Effects.What it catches
When a function-typed reactive value is read from an effect only inside a sub-handler, listing it in the dep array forces the whole effect to re-synchronize on every parent render. The fix is
useEffectEvent:Detector — four pre-conditions, all must hold
Chosen to keep real-world false positives near zero:
useEffectwith a dep array of ≥ 2 Identifier elements — single-dep effects don't meaningfully benefit from the migration.const F = useCallback(...)Finside the effect body sits inside a sub-handler:setTimeout/setInterval/requestAnimationFrame/requestIdleCallback/queueMicrotasksubscribe/addEventListener/addListener/on/watch/listen/subsetTimeout(() => …)) or via a const-bound local (const handler = () => …; window.addEventListener('keydown', handler);)Fis NEVER read at the effect's own top level — a synchronous call there is a true reactive read;useEffectEventisn't appropriate.React version gating
useEffectEventis a React 19+ Hook. Recommending it on React 18 (or older) projects produces noisy diagnostics for an API the user doesn't have, so the rule is registered in the existingVERSION_GATED_RULE_IDSmap under a newUSE_EFFECT_EVENT_MIN_MAJOR = 19constant.reactMajorVersion19(or higher)18,17(or lower)null(couldn't detect — workspace tag, missing dep, exotic spec)Tests — 12 regression cases
Detector behavior (8):
setTimeoutshape (Verceladvanced-use-latest)addEventListenerwith const-bound handler + multi-depsstore.subscribehandler that calls a prop callbackuseCallback-bound local invoked only fromsetIntervaladdEventListenerwith the callback as the only depVersion gating (4):
reactMajorVersion: 19→ firesreactMajorVersion: 18→ does not firereactMajorVersion: 17→ does not firereactMajorVersion: null(unknown) → firesPlus a smoke test in
run-oxlint.test.tsagainst a fixture component.Coverage map for the useEffect track
After this PR, the comprehensive useEffect analyzer family across all open PRs covers:
no-derived-state-effect,no-derived-useState,no-mirror-prop-effect(#157)no-derived-state-effectmemo branch (#153)no-derived-state-effect,no-set-state-in-renderno-effect-event-handlerwidened (#153)no-event-trigger-state(#155)no-effect-chain(#156)no-prop-callback-in-effectprefer-use-sync-external-store(#154)no-fetch-in-effectprefer-use-effect-event(this PR, React 19+)no-mutable-in-deps(#157)rerender-functional-setstateextended (#157)rerender-dependenciesextended (#157)effect-needs-cleanup(#157)Checks
493/493 tests passing locally. Lint, typecheck, format clean. No changeset (per project preference).