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
+}