Disallow calling hooks in functions
> Don’t call Hooks inside loops, conditions, or nested functions Per https://react.dev/warnings/invalid-hook-call-warning#breaking-rules-of-hooks it is invalid to call hooks inside function expressions. We now validate this by default, i'll verify internally before landing. Note the validation is somewhat more conservative and we only disallow known hook calls here, this seems like a reasonable tradeoff but i'm open to suggestions. We could reuse the same known/potential hook mechanism here but it would take some more refactoring.
Joe Savona committed
Feb 16, 2024 at 11:00 UTC
9c419dfd804efa4646784120790e606bb74bd905
33 files changed
+328
-410
compiler/packages/babel-plugin-react-forget/src/Validation/ValidateHooksUsage.ts
+61
-6
@@ -5,6 +5,7 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
+import * as t from "@babel/types";
9
import {
10
CompilerError,
11
CompilerErrorDetail,
@@ -89,21 +90,35 @@ function joinKinds(a: Kind, b: Kind): Kind {
90
export function validateHooksUsage(fn: HIRFunction): void {
91
const unconditionalBlocks = computeUnconditionalBlocks(fn);
92
92
- const errorsByPlace = new Map<SourceLocation, CompilerErrorDetail>();
93
+ const errors = new CompilerError();
94
+ const errorsByPlace = new Map<t.SourceLocation, CompilerErrorDetail>();
95
+
96
+ function recordError(
97
+ loc: SourceLocation,
98
+ errorDetail: CompilerErrorDetail
99
+ ): void {
100
+ if (typeof loc === "symbol") {
101
+ errors.pushErrorDetail(errorDetail);
102
+ } else {
103
+ errorsByPlace.set(loc, errorDetail);
104
+ }
105
+ }
106
+
107
function recordConditionalHookError(place: Place): void {
108
// Once a particular hook has a conditional call error, don't report any further issues for this hook
109
setKind(place, Kind.Error);
110
111
const reason =
112
"Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)";
99
- const previousError = errorsByPlace.get(place.loc);
113
+ const previousError =
114
+ typeof place.loc !== "symbol" ? errorsByPlace.get(place.loc) : undefined;
115
116
/*
117
* In some circumstances such as optional calls, we may first encounter a "hook may not be referenced as normal values" error.
118
* If that same place is also used as a conditional call, upgrade the error to a conditonal hook error
119
*/
120
if (previousError === undefined || previousError.reason !== reason) {
106
- errorsByPlace.set(
121
+ recordError(
122
place.loc,
123
new CompilerErrorDetail({
124
description: null,
@@ -117,8 +132,10 @@ export function validateHooksUsage(fn: HIRFunction): void {
132
}
133
}
134
function recordInvalidHookUsageError(place: Place): void {
120
- if (!errorsByPlace.has(place.loc)) {
121
- errorsByPlace.set(
135
+ const previousError =
136
+ typeof place.loc !== "symbol" ? errorsByPlace.get(place.loc) : undefined;
137
+ if (previousError === undefined) {
138
+ recordError(
139
place.loc,
140
new CompilerErrorDetail({
141
description: null,
@@ -348,6 +365,11 @@ export function validateHooksUsage(fn: HIRFunction): void {
365
}
366
break;
367
}
368
+ case "ObjectMethod":
369
+ case "FunctionExpression": {
370
+ visitFunctionExpression(errors, instr.value.loweredFunc.func);
371
+ break;
372
+ }
373
default: {
374
/*
375
* Else check usages of operands, but do *not* flow properties
@@ -369,7 +391,6 @@ export function validateHooksUsage(fn: HIRFunction): void {
391
}
392
}
393
372
- const errors = new CompilerError();
394
for (const [, error] of errorsByPlace) {
395
errors.push(error);
396
}
@@ -377,3 +398,37 @@ export function validateHooksUsage(fn: HIRFunction): void {
398
throw errors;
399
}
400
}
401
+
402
+function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
403
+ for (const [, block] of fn.body.blocks) {
404
+ for (const instr of block.instructions) {
405
+ switch (instr.value.kind) {
406
+ case "FunctionExpression": {
407
+ visitFunctionExpression(errors, instr.value.loweredFunc.func);
408
+ break;
409
+ }
410
+ case "MethodCall":
411
+ case "CallExpression": {
412
+ const callee =
413
+ instr.value.kind === "CallExpression"
414
+ ? instr.value.callee
415
+ : instr.value.property;
416
+ const hookKind = getHookKind(fn.env, callee.identifier);
417
+ if (hookKind != null) {
418
+ errors.pushErrorDetail(
419
+ new CompilerErrorDetail({
420
+ severity: ErrorSeverity.InvalidReact,
421
+ reason:
422
+ "Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)",
423
+ loc: callee.loc,
424
+ description: `Cannot call ${hookKind} within a function component`,
425
+ suggestions: null,
426
+ })
427
+ );
428
+ }
429
+ break;
430
+ }
431
+ }
432
+ }
433
+ }
434
+}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.bail.rules-of-hooks-3d692676194b.expect.md
new
+32
@@ -0,0 +1,32 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @skip
6
+// Unsupported input
7
+
8
+// Invalid because it's a common misunderstanding.
9
+// We *could* make it valid but the runtime error could be confusing.
10
+const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
11
+ useEffect(() => {
12
+ useHookInsideCallback();
13
+ });
14
+ return <button {...props} ref={ref} />;
15
+});
16
+
17
+```
18
+
19
+
20
+## Error
21
+
22
+```
23
+ 6 | const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
24
+ 7 | useEffect(() => {
25
+> 8 | useHookInsideCallback();
26
+ | ^^^^^^^^^^^^^^^^^^^^^ [ReactForget] InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call Custom within a function component (8:8)
27
+ 9 | });
28
+ 10 | return <button {...props} ref={ref} />;
29
+ 11 | });
30
+```
31
+
32
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.bail.rules-of-hooks-3d692676194b.js
renamed
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.bail.rules-of-hooks-8503ca76d6f8.expect.md
new
+32
@@ -0,0 +1,32 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @skip
6
+// Unsupported input
7
+
8
+// Invalid because it's a common misunderstanding.
9
+// We *could* make it valid but the runtime error could be confusing.
10
+const ComponentWithHookInsideCallback = React.memo((props) => {
11
+ useEffect(() => {
12
+ useHookInsideCallback();
13
+ });
14
+ return <button {...props} />;
15
+});
16
+
17
+```
18
+
19
+
20
+## Error
21
+
22
+```
23
+ 6 | const ComponentWithHookInsideCallback = React.memo((props) => {
24
+ 7 | useEffect(() => {
25
+> 8 | useHookInsideCallback();
26
+ | ^^^^^^^^^^^^^^^^^^^^^ [ReactForget] InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call Custom within a function component (8:8)
27
+ 9 | });
28
+ 10 | return <button {...props} />;
29
+ 11 | });
30
+```
31
+
32
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.bail.rules-of-hooks-8503ca76d6f8.js
renamed
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-0a1dbff27ba0.expect.md
new
+30
@@ -0,0 +1,30 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// Invalid because it's dangerous and might not warn otherwise.
6
+// This *must* be invalid.
7
+function createHook() {
8
+ return function useHookWithConditionalHook() {
9
+ if (cond) {
10
+ useConditionalHook();
11
+ }
12
+ };
13
+}
14
+
15
+```
16
+
17
+
18
+## Error
19
+
20
+```
21
+ 4 | return function useHookWithConditionalHook() {
22
+ 5 | if (cond) {
23
+> 6 | useConditionalHook();
24
+ | ^^^^^^^^^^^^^^^^^^ [ReactForget] InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call Custom within a function component (6:6)
25
+ 7 | }
26
+ 8 | };
27
+ 9 | }
28
+```
29
+
30
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-0a1dbff27ba0.js
renamed
-3
@@ -1,6 +1,3 @@
1
-// @skip
2
-// Passed but should have failed
3
-
1
// Invalid because it's dangerous and might not warn otherwise.
2
// This *must* be invalid.
3
function createHook() {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-0de1224ce64b.expect.md
new
+32
@@ -0,0 +1,32 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// Invalid because it's a common misunderstanding.
6
+// We *could* make it valid but the runtime error could be confusing.
7
+function createComponent() {
8
+ return function ComponentWithHookInsideCallback() {
9
+ useEffect(() => {
10
+ useHookInsideCallback();
11
+ });
12
+ };
13
+}
14
+
15
+```
16
+
17
+
18
+## Error
19
+
20
+```
21
+ 4 | return function ComponentWithHookInsideCallback() {
22
+ 5 | useEffect(() => {
23
+> 6 | useHookInsideCallback();
24
+ | ^^^^^^^^^^^^^^^^^^^^^ [ReactForget] InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call Custom within a function component (6:6)
25
+
26
+[ReactForget] InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call useEffect within a function component (5:5)
27
+ 7 | });
28
+ 8 | };
29
+ 9 | }
30
+```
31
+
32
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-0de1224ce64b.js
renamed
-3
@@ -1,6 +1,3 @@
1
-// @skip
2
-// Passed but should have failed
3
-
1
// Invalid because it's a common misunderstanding.
2
// We *could* make it valid but the runtime error could be confusing.
3
function createComponent() {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-449a37146a83.expect.md
new
+30
@@ -0,0 +1,30 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// Invalid because it's a common misunderstanding.
6
+// We *could* make it valid but the runtime error could be confusing.
7
+function createComponent() {
8
+ return function ComponentWithHookInsideCallback() {
9
+ function handleClick() {
10
+ useState();
11
+ }
12
+ };
13
+}
14
+
15
+```
16
+
17
+
18
+## Error
19
+
20
+```
21
+ 4 | return function ComponentWithHookInsideCallback() {
22
+ 5 | function handleClick() {
23
+> 6 | useState();
24
+ | ^^^^^^^^ [ReactForget] InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call useState within a function component (6:6)
25
+ 7 | }
26
+ 8 | };
27
+ 9 | }
28
+```
29
+
30
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-449a37146a83.js
renamed
-3
@@ -1,6 +1,3 @@
1
-// @skip
2
-// Passed but should have failed
3
-
1
// Invalid because it's a common misunderstanding.
2
// We *could* make it valid but the runtime error could be confusing.
3
function createComponent() {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-76a74b4666e9.expect.md
new
+28
@@ -0,0 +1,28 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// Invalid because it's a common misunderstanding.
6
+// We *could* make it valid but the runtime error could be confusing.
7
+function ComponentWithHookInsideCallback() {
8
+ function handleClick() {
9
+ useState();
10
+ }
11
+}
12
+
13
+```
14
+
15
+
16
+## Error
17
+
18
+```
19
+ 3 | function ComponentWithHookInsideCallback() {
20
+ 4 | function handleClick() {
21
+> 5 | useState();
22
+ | ^^^^^^^^ [ReactForget] InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call useState within a function component (5:5)
23
+ 6 | }
24
+ 7 | }
25
+ 8 |
26
+```
27
+
28
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-76a74b4666e9.js
renamed
-3
@@ -1,6 +1,3 @@
1
-// @skip
2
-// Passed but should have failed
3
-
1
// Invalid because it's a common misunderstanding.
2
// We *could* make it valid but the runtime error could be confusing.
3
function ComponentWithHookInsideCallback() {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-d842d36db450.expect.md
new
+30
@@ -0,0 +1,30 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// Invalid because it's dangerous and might not warn otherwise.
6
+// This *must* be invalid.
7
+function createComponent() {
8
+ return function ComponentWithConditionalHook() {
9
+ if (cond) {
10
+ useConditionalHook();
11
+ }
12
+ };
13
+}
14
+
15
+```
16
+
17
+
18
+## Error
19
+
20
+```
21
+ 4 | return function ComponentWithConditionalHook() {
22
+ 5 | if (cond) {
23
+> 6 | useConditionalHook();
24
+ | ^^^^^^^^^^^^^^^^^^ [ReactForget] InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call Custom within a function component (6:6)
25
+ 7 | }
26
+ 8 | };
27
+ 9 | }
28
+```
29
+
30
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-d842d36db450.js
renamed
-3
@@ -1,6 +1,3 @@
1
-// @skip
2
-// Passed but should have failed
3
-
1
// Invalid because it's dangerous and might not warn otherwise.
2
// This *must* be invalid.
3
function createComponent() {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-d952b82c2597.expect.md
new
+28
@@ -0,0 +1,28 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// Invalid because it's a common misunderstanding.
6
+// We *could* make it valid but the runtime error could be confusing.
7
+function ComponentWithHookInsideCallback() {
8
+ useEffect(() => {
9
+ useHookInsideCallback();
10
+ });
11
+}
12
+
13
+```
14
+
15
+
16
+## Error
17
+
18
+```
19
+ 3 | function ComponentWithHookInsideCallback() {
20
+ 4 | useEffect(() => {
21
+> 5 | useHookInsideCallback();
22
+ | ^^^^^^^^^^^^^^^^^^^^^ [ReactForget] InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call Custom within a function component (5:5)
23
+ 6 | });
24
+ 7 | }
25
+ 8 |
26
+```
27
+
28
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/error.invalid.invalid-rules-of-hooks-d952b82c2597.js
renamed
-3
@@ -1,6 +1,3 @@
1
-// @skip
2
-// Passed but should have failed
3
-
1
// Invalid because it's a common misunderstanding.
2
// We *could* make it valid but the runtime error could be confusing.
3
function ComponentWithHookInsideCallback() {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0592bd574811.expect.md
+2
@@ -2,6 +2,7 @@
2
## Input
3
4
```javascript
5
+// @compilationMode(infer)
6
// Regression test for some internal code.
7
// This shows how the "callback rule" is more relaxed,
8
// and doesn't kick in unless we're confident we're in
@@ -19,6 +20,7 @@ function makeListener(instance) {
20
## Code
21
22
```javascript
23
+// @compilationMode(infer)
24
// Regression test for some internal code.
25
// This shows how the "callback rule" is more relaxed,
26
// and doesn't kick in unless we're confident we're in
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-0592bd574811.js
+1
@@ -1,3 +1,4 @@
1
+// @compilationMode(infer)
2
// Regression test for some internal code.
3
// This shows how the "callback rule" is more relaxed,
4
// and doesn't kick in unless we're confident we're in
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-2bec02ac982b.expect.md
+7
-13
@@ -2,6 +2,7 @@
2
## Input
3
4
```javascript
5
+// @compilationMode(infer)
6
// Valid because hooks can call hooks.
7
function createHook() {
8
return function useHook() {
@@ -15,20 +16,13 @@ function createHook() {
16
## Code
17
18
```javascript
18
-import { unstable_useMemoCache as useMemoCache } from "react"; // Valid because hooks can call hooks.
19
+// @compilationMode(infer)
20
+// Valid because hooks can call hooks.
21
function createHook() {
20
- const $ = useMemoCache(1);
21
- let t0;
22
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
23
- t0 = function useHook() {
24
- useHook1();
25
- useHook2();
26
- };
27
- $[0] = t0;
28
- } else {
29
- t0 = $[0];
30
- }
31
- return t0;
22
+ return function useHook() {
23
+ useHook1();
24
+ useHook2();
25
+ };
26
}
27
28
```
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-2bec02ac982b.js
+1
@@ -1,3 +1,4 @@
1
+// @compilationMode(infer)
2
// Valid because hooks can call hooks.
3
function createHook() {
4
return function useHook() {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-33a6e23edac1.expect.md
+6
-12
@@ -2,6 +2,7 @@
2
## Input
3
4
```javascript
5
+// @compilationMode(infer)
6
// Valid because hooks can use hooks.
7
function createHook() {
8
return function useHookWithHook() {
@@ -14,19 +15,12 @@ function createHook() {
15
## Code
16
17
```javascript
17
-import { unstable_useMemoCache as useMemoCache } from "react"; // Valid because hooks can use hooks.
18
+// @compilationMode(infer)
19
+// Valid because hooks can use hooks.
20
function createHook() {
19
- const $ = useMemoCache(1);
20
- let t0;
21
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22
- t0 = function useHookWithHook() {
23
- useHook();
24
- };
25
- $[0] = t0;
26
- } else {
27
- t0 = $[0];
28
- }
29
- return t0;
21
+ return function useHookWithHook() {
22
+ useHook();
23
+ };
24
}
25
26
```
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-33a6e23edac1.js
+1
@@ -1,3 +1,4 @@
1
+// @compilationMode(infer)
2
// Valid because hooks can use hooks.
3
function createHook() {
4
return function useHookWithHook() {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-8f1c2c3f71c9.expect.md
+6
-12
@@ -2,6 +2,7 @@
2
## Input
3
4
```javascript
5
+// @compilationMode(infer)
6
// Valid because components can use hooks.
7
function createComponentWithHook() {
8
return function ComponentWithHook() {
@@ -14,19 +15,12 @@ function createComponentWithHook() {
15
## Code
16
17
```javascript
17
-import { unstable_useMemoCache as useMemoCache } from "react"; // Valid because components can use hooks.
18
+// @compilationMode(infer)
19
+// Valid because components can use hooks.
20
function createComponentWithHook() {
19
- const $ = useMemoCache(1);
20
- let t0;
21
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22
- t0 = function ComponentWithHook() {
23
- useHook();
24
- };
25
- $[0] = t0;
26
- } else {
27
- t0 = $[0];
28
- }
29
- return t0;
21
+ return function ComponentWithHook() {
22
+ useHook();
23
+ };
24
}
25
26
```
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-8f1c2c3f71c9.js
+1
@@ -1,3 +1,4 @@
1
+// @compilationMode(infer)
2
// Valid because components can use hooks.
3
function createComponentWithHook() {
4
return function ComponentWithHook() {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.bail.rules-of-hooks-3d692676194b.expect.md
deleted
-52
@@ -1,52 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @skip
6
-// Unsupported input
7
-
8
-// Invalid because it's a common misunderstanding.
9
-// We *could* make it valid but the runtime error could be confusing.
10
-const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
11
- useEffect(() => {
12
- useHookInsideCallback();
13
- });
14
- return <button {...props} ref={ref} />;
15
-});
16
-
17
-```
18
-
19
-## Code
20
-
21
-```javascript
22
-import { unstable_useMemoCache as useMemoCache } from "react"; // @skip
23
-// Unsupported input
24
-
25
-// Invalid because it's a common misunderstanding.
26
-// We *could* make it valid but the runtime error could be confusing.
27
-const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
28
- const $ = useMemoCache(4);
29
- let t0;
30
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
31
- t0 = () => {
32
- useHookInsideCallback();
33
- };
34
- $[0] = t0;
35
- } else {
36
- t0 = $[0];
37
- }
38
- useEffect(t0);
39
- let t1;
40
- if ($[1] !== props || $[2] !== ref) {
41
- t1 = <button {...props} ref={ref} />;
42
- $[1] = props;
43
- $[2] = ref;
44
- $[3] = t1;
45
- } else {
46
- t1 = $[3];
47
- }
48
- return t1;
49
-});
50
-
51
-```
52
-
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.bail.rules-of-hooks-8503ca76d6f8.expect.md
deleted
-51
@@ -1,51 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @skip
6
-// Unsupported input
7
-
8
-// Invalid because it's a common misunderstanding.
9
-// We *could* make it valid but the runtime error could be confusing.
10
-const ComponentWithHookInsideCallback = React.memo((props) => {
11
- useEffect(() => {
12
- useHookInsideCallback();
13
- });
14
- return <button {...props} />;
15
-});
16
-
17
-```
18
-
19
-## Code
20
-
21
-```javascript
22
-import { unstable_useMemoCache as useMemoCache } from "react"; // @skip
23
-// Unsupported input
24
-
25
-// Invalid because it's a common misunderstanding.
26
-// We *could* make it valid but the runtime error could be confusing.
27
-const ComponentWithHookInsideCallback = React.memo((props) => {
28
- const $ = useMemoCache(3);
29
- let t0;
30
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
31
- t0 = () => {
32
- useHookInsideCallback();
33
- };
34
- $[0] = t0;
35
- } else {
36
- t0 = $[0];
37
- }
38
- useEffect(t0);
39
- let t1;
40
- if ($[1] !== props) {
41
- t1 = <button {...props} />;
42
- $[1] = props;
43
- $[2] = t1;
44
- } else {
45
- t1 = $[2];
46
- }
47
- return t1;
48
-});
49
-
50
-```
51
-
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.invalid.invalid-rules-of-hooks-0a1dbff27ba0.expect.md
deleted
-45
@@ -1,45 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @skip
6
-// Passed but should have failed
7
-
8
-// Invalid because it's dangerous and might not warn otherwise.
9
-// This *must* be invalid.
10
-function createHook() {
11
- return function useHookWithConditionalHook() {
12
- if (cond) {
13
- useConditionalHook();
14
- }
15
- };
16
-}
17
-
18
-```
19
-
20
-## Code
21
-
22
-```javascript
23
-import { unstable_useMemoCache as useMemoCache } from "react"; // @skip
24
-// Passed but should have failed
25
-
26
-// Invalid because it's dangerous and might not warn otherwise.
27
-// This *must* be invalid.
28
-function createHook() {
29
- const $ = useMemoCache(1);
30
- let t0;
31
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32
- t0 = function useHookWithConditionalHook() {
33
- if (cond) {
34
- useConditionalHook();
35
- }
36
- };
37
- $[0] = t0;
38
- } else {
39
- t0 = $[0];
40
- }
41
- return t0;
42
-}
43
-
44
-```
45
-
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.invalid.invalid-rules-of-hooks-0de1224ce64b.expect.md
deleted
-45
@@ -1,45 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @skip
6
-// Passed but should have failed
7
-
8
-// Invalid because it's a common misunderstanding.
9
-// We *could* make it valid but the runtime error could be confusing.
10
-function createComponent() {
11
- return function ComponentWithHookInsideCallback() {
12
- useEffect(() => {
13
- useHookInsideCallback();
14
- });
15
- };
16
-}
17
-
18
-```
19
-
20
-## Code
21
-
22
-```javascript
23
-import { unstable_useMemoCache as useMemoCache } from "react"; // @skip
24
-// Passed but should have failed
25
-
26
-// Invalid because it's a common misunderstanding.
27
-// We *could* make it valid but the runtime error could be confusing.
28
-function createComponent() {
29
- const $ = useMemoCache(1);
30
- let t0;
31
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32
- t0 = function ComponentWithHookInsideCallback() {
33
- useEffect(() => {
34
- useHookInsideCallback();
35
- });
36
- };
37
- $[0] = t0;
38
- } else {
39
- t0 = $[0];
40
- }
41
- return t0;
42
-}
43
-
44
-```
45
-
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.invalid.invalid-rules-of-hooks-449a37146a83.expect.md
deleted
-41
@@ -1,41 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @skip
6
-// Passed but should have failed
7
-
8
-// Invalid because it's a common misunderstanding.
9
-// We *could* make it valid but the runtime error could be confusing.
10
-function createComponent() {
11
- return function ComponentWithHookInsideCallback() {
12
- function handleClick() {
13
- useState();
14
- }
15
- };
16
-}
17
-
18
-```
19
-
20
-## Code
21
-
22
-```javascript
23
-import { unstable_useMemoCache as useMemoCache } from "react"; // @skip
24
-// Passed but should have failed
25
-
26
-// Invalid because it's a common misunderstanding.
27
-// We *could* make it valid but the runtime error could be confusing.
28
-function createComponent() {
29
- const $ = useMemoCache(1);
30
- let t0;
31
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32
- t0 = function ComponentWithHookInsideCallback() {};
33
- $[0] = t0;
34
- } else {
35
- t0 = $[0];
36
- }
37
- return t0;
38
-}
39
-
40
-```
41
-
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.invalid.invalid-rules-of-hooks-76a74b4666e9.expect.md
deleted
-29
@@ -1,29 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @skip
6
-// Passed but should have failed
7
-
8
-// Invalid because it's a common misunderstanding.
9
-// We *could* make it valid but the runtime error could be confusing.
10
-function ComponentWithHookInsideCallback() {
11
- function handleClick() {
12
- useState();
13
- }
14
-}
15
-
16
-```
17
-
18
-## Code
19
-
20
-```javascript
21
-// @skip
22
-// Passed but should have failed
23
-
24
-// Invalid because it's a common misunderstanding.
25
-// We *could* make it valid but the runtime error could be confusing.
26
-function ComponentWithHookInsideCallback() {}
27
-
28
-```
29
-
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.invalid.invalid-rules-of-hooks-d842d36db450.expect.md
deleted
-45
@@ -1,45 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @skip
6
-// Passed but should have failed
7
-
8
-// Invalid because it's dangerous and might not warn otherwise.
9
-// This *must* be invalid.
10
-function createComponent() {
11
- return function ComponentWithConditionalHook() {
12
- if (cond) {
13
- useConditionalHook();
14
- }
15
- };
16
-}
17
-
18
-```
19
-
20
-## Code
21
-
22
-```javascript
23
-import { unstable_useMemoCache as useMemoCache } from "react"; // @skip
24
-// Passed but should have failed
25
-
26
-// Invalid because it's dangerous and might not warn otherwise.
27
-// This *must* be invalid.
28
-function createComponent() {
29
- const $ = useMemoCache(1);
30
- let t0;
31
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32
- t0 = function ComponentWithConditionalHook() {
33
- if (cond) {
34
- useConditionalHook();
35
- }
36
- };
37
- $[0] = t0;
38
- } else {
39
- t0 = $[0];
40
- }
41
- return t0;
42
-}
43
-
44
-```
45
-
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/todo.invalid.invalid-rules-of-hooks-d952b82c2597.expect.md
deleted
-41
@@ -1,41 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @skip
6
-// Passed but should have failed
7
-
8
-// Invalid because it's a common misunderstanding.
9
-// We *could* make it valid but the runtime error could be confusing.
10
-function ComponentWithHookInsideCallback() {
11
- useEffect(() => {
12
- useHookInsideCallback();
13
- });
14
-}
15
-
16
-```
17
-
18
-## Code
19
-
20
-```javascript
21
-import { unstable_useMemoCache as useMemoCache } from "react"; // @skip
22
-// Passed but should have failed
23
-
24
-// Invalid because it's a common misunderstanding.
25
-// We *could* make it valid but the runtime error could be confusing.
26
-function ComponentWithHookInsideCallback() {
27
- const $ = useMemoCache(1);
28
- let t0;
29
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30
- t0 = () => {
31
- useHookInsideCallback();
32
- };
33
- $[0] = t0;
34
- } else {
35
- t0 = $[0];
36
- }
37
- useEffect(t0);
38
-}
39
-
40
-```
41
-
\ No newline at end of file