Skip to content

[Compiler] Fix false positive for setState after await in useEffect#491

Closed
everettbu wants to merge 2 commits into
mainfrom
fix/set-state-in-effect-async-false-positive
Closed

[Compiler] Fix false positive for setState after await in useEffect#491
everettbu wants to merge 2 commits into
mainfrom
fix/set-state-in-effect-async-false-positive

Conversation

@everettbu

Copy link
Copy Markdown

Mirror of facebook/react#35732
Original author: basitqayoom


Summary

Fixes #34905

The set-state-in-effect validation (ValidateNoSetStateInEffects) was incorrectly flagging setState calls that appear after an await expression inside async functions called from useEffect.

After an await, execution continues asynchronously in a microtask — meaning the setState call is no longer synchronous within the effect body and should not trigger this lint.

Root cause

In getSetStateCall(), the function walks through the HIR of the effect callback and returns the first setState call it finds, without distinguishing whether that call appears after an Await instruction.

Fix

Added Await instruction tracking within each block in getSetStateCall():

  • When an Await instruction is encountered, a seenAwait flag is set for the current block
  • Any subsequent setState call in the same block after an Await is skipped (not reported as synchronous)

This is a conservative fix scoped to intra-block tracking. Cross-block await dominator analysis can be added as a follow-up if needed.

How did you test this change?

Added 3 new test fixtures:

Fixture Expected Description
allow-setState-in-useEffect-after-await ✅ No error setState after await via useCallback + useEffect (exact issue repro)
allow-setState-in-useEffect-async-callback ✅ No error setState after await in nested async function inside useEffect
invalid-setState-in-useEffect-before-await ❌ Error reported setState before await is still correctly flagged

All existing tests continue to pass:

$ yarn snap -- -p "invalid-setState-in-useEffect*"
5 Tests, 5 Passed, 0 Failed

$ yarn workspace eslint-plugin-react-compiler test
Test Suites: 8 passed, 8 total
Tests:       31 passed, 31 total

…#34905)

The `set-state-in-effect` validation was incorrectly flagging setState
calls that appear after an `await` expression inside async functions
called from useEffect. After an `await`, execution continues
asynchronously in a microtask, so the setState call is not synchronous
within the effect body.

This fix adds `Await` instruction tracking in `getSetStateCall()`. When
an `Await` instruction is encountered in a block, subsequent setState
calls in the same block are skipped since they execute asynchronously.

Test plan:
- allow-setState-in-useEffect-after-await: setState after await via
  useCallback + useEffect (exact issue repro) - no error
- allow-setState-in-useEffect-async-callback: setState after await in
  nested async function inside useEffect - no error
- invalid-setState-in-useEffect-before-await: setState before await is
  still correctly flagged - error reported

Fixes #34905

Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

greptile-apps Bot commented Feb 9, 2026

Copy link
Copy Markdown

Greptile Overview

Greptile Summary

This PR adjusts the ValidateNoSetStateInEffects validation to stop reporting setState calls that occur after an Await instruction when scanning the HIR for synchronous effect-body updates. The change tracks a per-block seenAwait flag in getSetStateCall() and skips setState call detection once an Await has been observed, which aligns with the intent to only flag synchronous effect execution.

It also adds three compiler snapshot fixtures that cover (1) setState after await in an async callback invoked from useEffect, (2) setState after await in a nested async function inside useEffect, and (3) a regression case where setState before await is still correctly flagged.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk.
  • The change is narrowly scoped to the validation scan logic (skipping setState detection only after an Await in the same block) and is covered by new fixtures that exercise both the fixed false-positive and a regression case (setState before await remains flagged). No behavioral changes occur outside this specific compiler validation.
  • compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts

Important Files Changed

Filename Overview
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts Adds intra-block tracking of HIR 'Await' to avoid flagging setState calls that occur after an await inside effect-related async functions.
compiler/packages/babel-plugin-react-compiler/src/tests/fixtures/compiler/allow-setState-in-useEffect-after-await.expect.md Adds snapshot for new fixture demonstrating setState after await is allowed; output shows successful compilation with no lint error.
compiler/packages/babel-plugin-react-compiler/src/tests/fixtures/compiler/allow-setState-in-useEffect-after-await.js Adds new fixture input for setState after await via async useCallback invoked from useEffect.
compiler/packages/babel-plugin-react-compiler/src/tests/fixtures/compiler/allow-setState-in-useEffect-async-callback.expect.md Adds snapshot for nested async function in useEffect where setState occurs after await and should be allowed.
compiler/packages/babel-plugin-react-compiler/src/tests/fixtures/compiler/allow-setState-in-useEffect-async-callback.js Adds fixture for nested async function inside useEffect with setState after await.
compiler/packages/babel-plugin-react-compiler/src/tests/fixtures/compiler/invalid-setState-in-useEffect-before-await.expect.md Adds snapshot asserting setState before await is still reported as an EffectSetState error.
compiler/packages/babel-plugin-react-compiler/src/tests/fixtures/compiler/invalid-setState-in-useEffect-before-await.js Adds fixture input where async callback calls setState before await, which should remain invalid.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@everettbu

Copy link
Copy Markdown
Author

Upstream PR was closed or merged. Code is synced via branch mirror.

@everettbu everettbu closed this Feb 22, 2026
@everettbu
everettbu deleted the fix/set-state-in-effect-async-false-positive branch February 22, 2026 17:20
@everettbu
everettbu restored the fix/set-state-in-effect-async-false-positive branch February 22, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants