mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix/review bug repros, mark fixed
I reviewed the test cases we have marked as bugs ("_bug.*") and realized that
several of them are already fixed — woohoo! Then one wasn't fixed _yet_: our
type inference loses track of refs if you stash them inside an object/array. But
that's why I added the ValidateNoRefAccessInRender pass, which i've updated to
detect and reject these invalid cases. There are now only a few bugs left (more
fixes coming).
This commit is contained in:
@@ -40,6 +40,15 @@ export function validateNoRefAccessInRender(fn: HIRFunction): void {
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
switch (instr.value.kind) {
|
||||
case "PropertyLoad":
|
||||
case "LoadLocal":
|
||||
case "StoreLocal":
|
||||
case "Destructure": {
|
||||
// These instructions are necessary for storing the results of a useRef into
|
||||
// a variable and referencing them in functions. We can propagate type info
|
||||
// for these instructions so they ensure we have a complete analysis.
|
||||
break;
|
||||
}
|
||||
case "FunctionExpression": {
|
||||
// For now we assume *all* function expressions are safe, eventually we can
|
||||
// be more precise and disallow ref access in functions that may be called
|
||||
@@ -57,6 +66,7 @@ export function validateNoRefAccessInRender(fn: HIRFunction): void {
|
||||
default: {
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
validateNonRefValue(error, operand);
|
||||
validateNonRefObject(error, operand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Foo({ a }) {
|
||||
const ref = useRef();
|
||||
// type information is lost here as we don't track types of fields
|
||||
const val = { ref };
|
||||
// without type info, we don't know that val.ref.current is a ref value so we
|
||||
// end up depending on val.ref.current
|
||||
const x = { a, val: val.ref.current };
|
||||
|
||||
return <VideoList videos={x} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Foo(t23) {
|
||||
const $ = useMemoCache(7);
|
||||
const { a } = t23;
|
||||
const ref = useRef();
|
||||
const c_0 = $[0] !== ref;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
t0 = { ref };
|
||||
$[0] = ref;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const val = t0;
|
||||
const c_2 = $[2] !== a;
|
||||
const c_3 = $[3] !== val.ref.current;
|
||||
let t1;
|
||||
if (c_2 || c_3) {
|
||||
t1 = { a, val: val.ref.current };
|
||||
$[2] = a;
|
||||
$[3] = val.ref.current;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
const x = t1;
|
||||
const c_5 = $[5] !== x;
|
||||
let t2;
|
||||
if (c_5) {
|
||||
t2 = <VideoList videos={x} />;
|
||||
$[5] = x;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
-4
@@ -15,10 +15,6 @@ function Component(props) {
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInput: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at <unknown> $20:TObject<BuiltInRefValue> (4:4)
|
||||
|
||||
[ReactForget] InvalidInput: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at <unknown> value$21:TObject<BuiltInRefValue> (5:5)
|
||||
|
||||
[ReactForget] InvalidInput: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at <unknown> $23:TObject<BuiltInRefValue> (5:5)
|
||||
```
|
||||
|
||||
|
||||
+2
@@ -14,6 +14,8 @@ function Component(props) {
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInput: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. Cannot access ref object at <unknown> $22:TObject<BuiltInUseRefId> (3:3)
|
||||
|
||||
[ReactForget] InvalidInput: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at <unknown> $25:TObject<BuiltInRefValue> (4:4)
|
||||
```
|
||||
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Foo({ a }) {
|
||||
const ref = useRef();
|
||||
// type information is lost here as we don't track types of fields
|
||||
const val = { ref };
|
||||
// without type info, we don't know that val.ref.current is a ref value so we
|
||||
// *would* end up depending on val.ref.current
|
||||
// however, this is an instance of accessing a ref during render and is disallowed
|
||||
// under React's rules, so we reject this input
|
||||
const x = { a, val: val.ref.current };
|
||||
|
||||
return <VideoList videos={x} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInput: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. Cannot access ref object at <unknown> $30:TObject<BuiltInUseRefId> (4:4)
|
||||
```
|
||||
|
||||
|
||||
+3
-1
@@ -3,7 +3,9 @@ function Foo({ a }) {
|
||||
// type information is lost here as we don't track types of fields
|
||||
const val = { ref };
|
||||
// without type info, we don't know that val.ref.current is a ref value so we
|
||||
// end up depending on val.ref.current
|
||||
// *would* end up depending on val.ref.current
|
||||
// however, this is an instance of accessing a ref during render and is disallowed
|
||||
// under React's rules, so we reject this input
|
||||
const x = { a, val: val.ref.current };
|
||||
|
||||
return <VideoList videos={x} />;
|
||||
Reference in New Issue
Block a user