-
Notifications
You must be signed in to change notification settings - Fork 47k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[compiler] Fix: ref.current now correctly reactive (#31521)
We were previously filtering out `ref.current` dependencies in propagateScopeDependencies:checkValidDependency`. This is incorrect. Instead, we now always take a dependency on ref values (the outer box) as they may be reactive. Pruning is done in pruneNonReactiveDependencies. This PR includes a small patch to `collectReactiveIdentifier`. Prior to this, we conservatively assumed that pruned scopes always produced reactive declarations. This assumption fixed a bug with non-reactivity, but some of these declarations are `useRef` calls. Now we have special handling for this case ```js // This often produces a pruned scope React.useRef(1); ``` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31521). * #31202 * #31203 * #31201 * #31200 * __->__ #31521
- Loading branch information
Showing
11 changed files
with
272 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 0 additions & 89 deletions
89
...in-react-compiler/src/__tests__/fixtures/compiler/bug-nonreactive-ref.expect.md
This file was deleted.
Oops, something went wrong.
33 changes: 0 additions & 33 deletions
33
...kages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-nonreactive-ref.tsx
This file was deleted.
Oops, something went wrong.
102 changes: 102 additions & 0 deletions
102
...gin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref-param.expect.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
|
||
## Input | ||
|
||
```javascript | ||
import {useRef, forwardRef} from 'react'; | ||
import {Stringify} from 'shared-runtime'; | ||
|
||
/** | ||
* Fixture showing that Ref types may be reactive. | ||
* We should always take a dependency on ref values (the outer box) as | ||
* they may be reactive. Pruning should be done in | ||
* `pruneNonReactiveDependencies` | ||
*/ | ||
|
||
function Parent({cond}) { | ||
const ref1 = useRef(1); | ||
const ref2 = useRef(2); | ||
const ref = cond ? ref1 : ref2; | ||
return <Child ref={ref} />; | ||
} | ||
|
||
function ChildImpl(_props, ref) { | ||
const cb = () => ref.current; | ||
return <Stringify cb={cb} shouldInvokeFns={true} />; | ||
} | ||
|
||
const Child = forwardRef(ChildImpl); | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Parent, | ||
params: [{cond: true}], | ||
sequentialRenders: [{cond: true}, {cond: false}], | ||
}; | ||
|
||
``` | ||
|
||
## Code | ||
|
||
```javascript | ||
import { c as _c } from "react/compiler-runtime"; | ||
import { useRef, forwardRef } from "react"; | ||
import { Stringify } from "shared-runtime"; | ||
|
||
/** | ||
* Fixture showing that Ref types may be reactive. | ||
* We should always take a dependency on ref values (the outer box) as | ||
* they may be reactive. Pruning should be done in | ||
* `pruneNonReactiveDependencies` | ||
*/ | ||
|
||
function Parent(t0) { | ||
const $ = _c(2); | ||
const { cond } = t0; | ||
const ref1 = useRef(1); | ||
const ref2 = useRef(2); | ||
const ref = cond ? ref1 : ref2; | ||
let t1; | ||
if ($[0] !== ref) { | ||
t1 = <Child ref={ref} />; | ||
$[0] = ref; | ||
$[1] = t1; | ||
} else { | ||
t1 = $[1]; | ||
} | ||
return t1; | ||
} | ||
|
||
function ChildImpl(_props, ref) { | ||
const $ = _c(4); | ||
let t0; | ||
if ($[0] !== ref) { | ||
t0 = () => ref.current; | ||
$[0] = ref; | ||
$[1] = t0; | ||
} else { | ||
t0 = $[1]; | ||
} | ||
const cb = t0; | ||
let t1; | ||
if ($[2] !== cb) { | ||
t1 = <Stringify cb={cb} shouldInvokeFns={true} />; | ||
$[2] = cb; | ||
$[3] = t1; | ||
} else { | ||
t1 = $[3]; | ||
} | ||
return t1; | ||
} | ||
|
||
const Child = forwardRef(ChildImpl); | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Parent, | ||
params: [{ cond: true }], | ||
sequentialRenders: [{ cond: true }, { cond: false }], | ||
}; | ||
|
||
``` | ||
### Eval output | ||
(kind: ok) <div>{"cb":{"kind":"Function","result":1},"shouldInvokeFns":true}</div> | ||
<div>{"cb":{"kind":"Function","result":2},"shouldInvokeFns":true}</div> |
29 changes: 29 additions & 0 deletions
29
...ckages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref-param.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {useRef, forwardRef} from 'react'; | ||
import {Stringify} from 'shared-runtime'; | ||
|
||
/** | ||
* Fixture showing that Ref types may be reactive. | ||
* We should always take a dependency on ref values (the outer box) as | ||
* they may be reactive. Pruning should be done in | ||
* `pruneNonReactiveDependencies` | ||
*/ | ||
|
||
function Parent({cond}) { | ||
const ref1 = useRef(1); | ||
const ref2 = useRef(2); | ||
const ref = cond ? ref1 : ref2; | ||
return <Child ref={ref} />; | ||
} | ||
|
||
function ChildImpl(_props, ref) { | ||
const cb = () => ref.current; | ||
return <Stringify cb={cb} shouldInvokeFns={true} />; | ||
} | ||
|
||
const Child = forwardRef(ChildImpl); | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Parent, | ||
params: [{cond: true}], | ||
sequentialRenders: [{cond: true}, {cond: false}], | ||
}; |
Oops, something went wrong.