mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Add detection of dynamic hooks
ghstack-source-id: 3acfa4fde5dfd65353fa407378057ca937ee3599 Pull Request resolved: https://github.com/facebook/react-forget/pull/2901
This commit is contained in:
@@ -122,8 +122,7 @@ export function validateHooksUsage(fn: HIRFunction): void {
|
||||
place.loc,
|
||||
new CompilerErrorDetail({
|
||||
description: null,
|
||||
reason:
|
||||
"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)",
|
||||
reason,
|
||||
loc: place.loc,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
@@ -140,7 +139,24 @@ export function validateHooksUsage(fn: HIRFunction): void {
|
||||
new CompilerErrorDetail({
|
||||
description: null,
|
||||
reason:
|
||||
"Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)",
|
||||
"Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values",
|
||||
loc: place.loc,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
function recordDynamicHookUsageError(place: Place): void {
|
||||
const previousError =
|
||||
typeof place.loc !== "symbol" ? errorsByPlace.get(place.loc) : undefined;
|
||||
if (previousError === undefined) {
|
||||
recordError(
|
||||
place.loc,
|
||||
new CompilerErrorDetail({
|
||||
description: null,
|
||||
reason:
|
||||
"Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks",
|
||||
loc: place.loc,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
@@ -224,7 +240,10 @@ export function validateHooksUsage(fn: HIRFunction): void {
|
||||
case "StoreLocal":
|
||||
case "StoreContext": {
|
||||
visitPlace(instr.value.value);
|
||||
const kind = getKindForPlace(instr.value.value);
|
||||
const kind = joinKinds(
|
||||
getKindForPlace(instr.value.value),
|
||||
getKindForPlace(instr.value.lvalue.place)
|
||||
);
|
||||
setKind(instr.value.lvalue.place, kind);
|
||||
setKind(instr.lvalue, kind);
|
||||
break;
|
||||
@@ -298,10 +317,11 @@ export function validateHooksUsage(fn: HIRFunction): void {
|
||||
calleeKind === Kind.KnownHook || calleeKind === Kind.PotentialHook;
|
||||
if (isHookCallee && !unconditionalBlocks.has(block.id)) {
|
||||
recordConditionalHookError(instr.value.callee);
|
||||
} else if (calleeKind === Kind.PotentialHook) {
|
||||
recordDynamicHookUsageError(instr.value.callee);
|
||||
}
|
||||
/**
|
||||
* We intentionally skip the callee because known/potential hooks
|
||||
* are always allowed to be called.
|
||||
* We intentionally skip the callee because it's validated above
|
||||
*/
|
||||
for (const operand of eachInstructionOperand(instr)) {
|
||||
if (operand === instr.value.callee) {
|
||||
@@ -317,10 +337,11 @@ export function validateHooksUsage(fn: HIRFunction): void {
|
||||
calleeKind === Kind.KnownHook || calleeKind === Kind.PotentialHook;
|
||||
if (isHookCallee && !unconditionalBlocks.has(block.id)) {
|
||||
recordConditionalHookError(instr.value.property);
|
||||
} else if (calleeKind === Kind.PotentialHook) {
|
||||
recordDynamicHookUsageError(instr.value.property);
|
||||
}
|
||||
/*
|
||||
* We intentionally skip the callee because known/potential hooks
|
||||
* are always allowed to be called as methods (`React.useState()`).
|
||||
* We intentionally skip the property because it's validated above
|
||||
*/
|
||||
for (const operand of eachInstructionOperand(instr)) {
|
||||
if (operand === instr.value.property) {
|
||||
|
||||
+2
-2
@@ -26,9 +26,9 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
5 |
|
||||
6 | function Foo() {
|
||||
> 7 | let bar = useFoo.useBar;
|
||||
| ^^^^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (7:7)
|
||||
| ^^^^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (7:7)
|
||||
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (8:8)
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (8:8)
|
||||
8 | return bar();
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ function Component(props) {
|
||||
```
|
||||
1 | function Component(props) {
|
||||
> 2 | const x = useState;
|
||||
| ^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2)
|
||||
| ^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2)
|
||||
3 | const state = x(null);
|
||||
4 | return state[0];
|
||||
5 | }
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ function Component(props) {
|
||||
```
|
||||
1 | function Component(props) {
|
||||
> 2 | return foo(useFoo);
|
||||
| ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2)
|
||||
| ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2)
|
||||
3 | }
|
||||
4 |
|
||||
```
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ function Component(props) {
|
||||
```
|
||||
1 | function Component(props) {
|
||||
> 2 | return <Child foo={useFoo} />;
|
||||
| ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2)
|
||||
| ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2)
|
||||
3 | }
|
||||
4 |
|
||||
```
|
||||
|
||||
+4
-4
@@ -15,13 +15,13 @@ function Component(props) {
|
||||
```
|
||||
1 | function Component(props) {
|
||||
> 2 | const x = props.cond ? useA : useB;
|
||||
| ^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2)
|
||||
| ^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2)
|
||||
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2)
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2)
|
||||
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2)
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2)
|
||||
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3)
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3)
|
||||
3 | return x();
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
+2
-2
@@ -15,9 +15,9 @@ function Component() {
|
||||
```
|
||||
1 | function Component() {
|
||||
> 2 | const x = Foo.useFoo;
|
||||
| ^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2)
|
||||
| ^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2)
|
||||
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3)
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3)
|
||||
3 | return x();
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
+3
-3
@@ -21,11 +21,11 @@ function Component(props) {
|
||||
1 | function Component(props) {
|
||||
2 | // This is a violation of using a hook as a normal value rule:
|
||||
> 3 | const getUser = props.cond ? useGetUser : emptyFunction;
|
||||
| ^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3)
|
||||
| ^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3)
|
||||
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3)
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3)
|
||||
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (8:8)
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (8:8)
|
||||
4 |
|
||||
5 | // Ideally we would report a "conditional hook call" error here.
|
||||
6 | // It's an unconditional call, but the value may or may not be a hook.
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component() {
|
||||
const someFunction = useContext(FooContext);
|
||||
const useOhItsNamedLikeAHookNow = someFunction;
|
||||
useOhItsNamedLikeAHookNow();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
2 | const someFunction = useContext(FooContext);
|
||||
3 | const useOhItsNamedLikeAHookNow = someFunction;
|
||||
> 4 | useOhItsNamedLikeAHookNow();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks (4:4)
|
||||
5 | }
|
||||
6 |
|
||||
```
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
function Component() {
|
||||
const someFunction = useContext(FooContext);
|
||||
const useOhItsNamedLikeAHookNow = someFunction;
|
||||
useOhItsNamedLikeAHookNow();
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@ function Component(props) {
|
||||
```
|
||||
1 | function Component(props) {
|
||||
> 2 | const x = props.cond ? (useFoo ? 1 : 2) : 3;
|
||||
| ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (2:2)
|
||||
| ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2)
|
||||
3 | return x;
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component({ useFoo }) {
|
||||
useFoo();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function Component({ useFoo }) {
|
||||
> 2 | useFoo();
|
||||
| ^^^^^^ InvalidReact: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks (2:2)
|
||||
3 | }
|
||||
4 |
|
||||
```
|
||||
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
function Component({ useFoo }) {
|
||||
useFoo();
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function useFoo({ data }) {
|
||||
const useMedia = useVideoPlayer();
|
||||
const foo = useMedia();
|
||||
return foo;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function useFoo({ data }) {
|
||||
2 | const useMedia = useVideoPlayer();
|
||||
> 3 | const foo = useMedia();
|
||||
| ^^^^^^^^ InvalidReact: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks (3:3)
|
||||
4 | return foo;
|
||||
5 | }
|
||||
6 |
|
||||
```
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
function useFoo({ data }) {
|
||||
const useMedia = useVideoPlayer();
|
||||
const foo = useMedia();
|
||||
return foo;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function useFoo({ data }) {
|
||||
const player = useVideoPlayer();
|
||||
const foo = player.useMedia();
|
||||
return foo;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function useFoo({ data }) {
|
||||
2 | const player = useVideoPlayer();
|
||||
> 3 | const foo = player.useMedia();
|
||||
| ^^^^^^^^^^^^^^^ InvalidReact: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks (3:3)
|
||||
4 | return foo;
|
||||
5 | }
|
||||
6 |
|
||||
```
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
function useFoo({ data }) {
|
||||
const player = useVideoPlayer();
|
||||
const foo = player.useMedia();
|
||||
return foo;
|
||||
}
|
||||
+3
-3
@@ -17,11 +17,11 @@ function Component(props) {
|
||||
1 | function Component(props) {
|
||||
2 | let y;
|
||||
> 3 | props.cond ? (y = useFoo) : null;
|
||||
| ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3)
|
||||
| ^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3)
|
||||
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (3:3)
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (3:3)
|
||||
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (4:4)
|
||||
InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (4:4)
|
||||
4 | return y();
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
Reference in New Issue
Block a user