@samitouri / QOS-React-1 / commits / 22a20e1f2f

[compiler] Fix setState-in-effect for React.useEffect namespace calls (#35377) (#35419)

## Summary Fix react-hooks/set-state-in-effect false negatives when Hooks are called via a namespace import (e.g. `import * as React from 'react'` and `React.useEffect(...))`. The validation now checks the MethodCall property (the actual hook function) instead of the receiver object. Issue: Bug: #35377 ## How did you test this change? Added a regression fixture; Ran tests and verified it reports `EffectSetState` and matches the expected output. <img width="461" height="116" alt="Screenshot 2025-12-27 at 14 13 38" src="https://github.com/user-attachments/assets/fff5aab9-0f2c-40e9-a6a5-b864c3fa6fbd" />

Anton Chesnokov committed Feb 4, 2026 at 20:07 UTC 22a20e1f2f557b99115d82b639ff5a32b6453cb6
3 files changed +53 -1
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts
+1 -1
@@ -97,7 +97,7 @@ export function validateNoSetStateInEffects(
97 case 'CallExpression': {
98 const callee =
99 instr.value.kind === 'MethodCall'
100 - ? instr.value.receiver
100 + ? instr.value.property
101 : instr.value.callee;
102
103 if (isUseEffectEventType(callee.identifier)) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-namespace.expect.md new
+42
@@ -0,0 +1,42 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint"
6 +import * as React from 'react';
7 +
8 +function Component() {
9 + const [state, setState] = React.useState(0);
10 + React.useEffect(() => {
11 + setState(s => s + 1);
12 + });
13 + return state;
14 +}
15 +
16 +```
17 +
18 +## Code
19 +
20 +```javascript
21 +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint"
22 +import * as React from "react";
23 +
24 +function Component() {
25 + const [state, setState] = React.useState(0);
26 + React.useEffect(() => {
27 + setState((s) => s + 1);
28 + });
29 + return state;
30 +}
31 +
32 +```
33 +
34 +## Logs
35 +
36 +```
37 +{"kind":"CompileError","detail":{"options":{"category":"EffectSetState","reason":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":7,"column":4,"index":200},"end":{"line":7,"column":12,"index":208},"filename":"invalid-setState-in-useEffect-namespace.ts","identifierName":"setState"},"message":"Avoid calling setState() directly within an effect"}]}},"fnLoc":null}
38 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":100},"end":{"line":10,"column":1,"index":245},"filename":"invalid-setState-in-useEffect-namespace.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":1,"prunedMemoValues":1}
39 +```
40 +
41 +### Eval output
42 +(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-namespace.js new
+10
@@ -0,0 +1,10 @@
1 +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint"
2 +import * as React from 'react';
3 +
4 +function Component() {
5 + const [state, setState] = React.useState(0);
6 + React.useEffect(() => {
7 + setState(s => s + 1);
8 + });
9 + return state;
10 +}