Flag ValidateNoSetStateInRender
This needs a bit more work before we can turn it on by default, see the next PR for a failing test case.
Lauren Tan committed
Jul 27, 2023 at 17:08 UTC
e77d975b169a4e768e2efec2a506eb4a1bd1090d
3 files changed
+24
-6
compiler/forget/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts
+8
-6
@@ -137,12 +137,14 @@ export function* run(
137
validateNoRefAccessInRender(hir);
138
}
139
140
- const noSetStateInRenderResult = validateNoSetStateInRender(hir).unwrap();
141
- yield log({
142
- kind: "debug",
143
- name: "ValidateNoSetStateInRender",
144
- value: noSetStateInRenderResult.debug(),
145
- });
140
+ if (env.validateNoSetStateInRender) {
141
+ const noSetStateInRenderResult = validateNoSetStateInRender(hir).unwrap();
142
+ yield log({
143
+ kind: "debug",
144
+ name: "ValidateNoSetStateInRender",
145
+ value: noSetStateInRenderResult.debug(),
146
+ });
147
+ }
148
149
leaveSSA(hir);
150
yield log({ kind: "hir", name: "LeaveSSA", value: hir });
compiler/forget/packages/babel-plugin-react-forget/src/HIR/Environment.ts
+11
@@ -83,6 +83,14 @@ export type EnvironmentConfig = Partial<{
83
*/
84
validateFrozenLambdas: boolean;
85
86
+ /**
87
+ * Validates that setState is not unconditionally called during render, as it can lead to
88
+ * infinite loops.
89
+ *
90
+ * Defaults to false
91
+ */
92
+ validateNoSetStateInRender: boolean;
93
+
94
/**
95
* Enable inlining of `useMemo()` function expressions so that they can be more optimally
96
* compiled.
@@ -176,6 +184,7 @@ export class Environment {
184
validateHooksUsage: boolean;
185
validateRefAccessDuringRender: boolean;
186
validateFrozenLambdas: boolean;
187
+ validateNoSetStateInRender: boolean;
188
enableFunctionCallSignatureOptimizations: boolean;
189
enableAssumeHooksFollowRulesOfReact: boolean;
190
enableTreatHooksAsFunctions: boolean;
@@ -231,6 +240,8 @@ export class Environment {
240
this.enableOptimizeFunctionExpressions =
241
config?.enableOptimizeFunctionExpressions ?? true;
242
this.assertValidMutableRanges = config?.assertValidMutableRanges ?? false;
243
+ this.validateNoSetStateInRender =
244
+ config?.validateNoSetStateInRender ?? false;
245
246
this.#contextIdentifiers = contextIdentifiers;
247
}
compiler/forget/packages/snap/src/compiler-worker.ts
+5
@@ -97,6 +97,7 @@ export async function compile(
97
let enableTreatHooksAsFunctions = true;
98
let disableAllMemoization = false;
99
let validateRefAccessDuringRender = true;
100
+ let validateNoSetStateInRender = true;
101
let enableEmitFreeze = null;
102
let enableOptimizeFunctionExpressions = true;
103
if (firstLine.indexOf("@forgetDirective") !== -1) {
@@ -132,6 +133,9 @@ export async function compile(
133
if (firstLine.includes("@validateRefAccessDuringRender false")) {
134
validateRefAccessDuringRender = false;
135
}
136
+ if (firstLine.includes("@validateNoSetStateInRender false")) {
137
+ validateNoSetStateInRender = false;
138
+ }
139
if (firstLine.includes("@enableOptimizeFunctionExpressions false")) {
140
enableOptimizeFunctionExpressions = false;
141
}
@@ -165,6 +169,7 @@ export async function compile(
169
validateHooksUsage: true,
170
validateRefAccessDuringRender,
171
validateFrozenLambdas: true,
172
+ validateNoSetStateInRender,
173
enableEmitFreeze,
174
enableOptimizeFunctionExpressions,
175
assertValidMutableRanges: true,