@samitouri / QOS-React-1 / commits / 5230e6d849

Add failing test case for ValidateNoSetStateInRender bug

The current heuristic to check if setState is called in render is based on whether the lambda containing the call to setState has a mutable range that got extended. This doesn't seem to work in all cases so this validation needs a bit more work before we can turn it on by default

Lauren Tan committed Jul 27, 2023 at 17:08 UTC 5230e6d8491a21b8c4566d503d05bf05065e5185
2 files changed +63
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.bug-validate-no-set-state-not-all-mutable-range-extensions-are-bad.expect.md new
+39
@@ -0,0 +1,39 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const logEvent = useLogging(props.appId);
7 + const [currentStep, setCurrentStep] = useState(0);
8 +
9 + const onSubmit = (errorEvent) => {
10 + // 2. onSubmit inherits the mutable range of logEvent
11 + logEvent(errorEvent);
12 + // 3. this call then triggers the ValidateNoSetStateInRender check incorrectly, even though
13 + // onSubmit is not called during render (although it _could_ be, if OtherComponent does so.
14 + // but we can't tell without x-file analysis)
15 + setCurrentStep(1);
16 + };
17 +
18 + switch (currentStep) {
19 + case 0:
20 + return <OtherComponent data={{ foo: "bar" }} />;
21 + case 1:
22 + return <OtherComponent data={{ foo: "joe" }} onSubmit={onSubmit} />;
23 + default:
24 + // 1. logEvent's mutable range is extended to this instruction
25 + logEvent("Invalid step");
26 + return <OtherComponent data={null} />;
27 + }
28 +}
29 +
30 +```
31 +
32 +
33 +## Error
34 +
35 +```
36 +[ReactForget] InvalidReact: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) (11:11)
37 +```
38 +
39 +
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.bug-validate-no-set-state-not-all-mutable-range-extensions-are-bad.js new
+24
@@ -0,0 +1,24 @@
1 +function Component(props) {
2 + const logEvent = useLogging(props.appId);
3 + const [currentStep, setCurrentStep] = useState(0);
4 +
5 + const onSubmit = (errorEvent) => {
6 + // 2. onSubmit inherits the mutable range of logEvent
7 + logEvent(errorEvent);
8 + // 3. this call then triggers the ValidateNoSetStateInRender check incorrectly, even though
9 + // onSubmit is not called during render (although it _could_ be, if OtherComponent does so.
10 + // but we can't tell without x-file analysis)
11 + setCurrentStep(1);
12 + };
13 +
14 + switch (currentStep) {
15 + case 0:
16 + return <OtherComponent data={{ foo: "bar" }} />;
17 + case 1:
18 + return <OtherComponent data={{ foo: "joe" }} onSubmit={onSubmit} />;
19 + default:
20 + // 1. logEvent's mutable range is extended to this instruction
21 + logEvent("Invalid step");
22 + return <OtherComponent data={null} />;
23 + }
24 +}