-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[compiler] Fix: ref.current now correctly reactive #31521
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
maybeDependency = { | ||
identifier: maybeDependency.identifier, | ||
path: [], | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now we explicitly downgrade ref.current.a.b
dependencies to just ref
.
if (isRefValueType(maybeDependency.identifier)) { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a little weird. Ideally we should take the ref
type producing the ref.current
read as a dependency, but this seems like an edge case (we don't allow ref.current
reads in render anyways).
Happy to turn this into an invariant instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah interesting. seems worth trying to add an invariant here in a follow-up and see if there's any place it gets hit
(kind: ok) <div>{"cb":{"kind":"Function","result":1},"shouldInvokeFns":true}</div> | ||
<div>{"cb":{"kind":"Function","result":2},"shouldInvokeFns":true}</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that here, the re-render receives a different ref
argument (i.e. ref parameters should be considered reactive)
if ($[0] === Symbol.for("react.memo_cache_sentinel")) { | ||
if ($[0] !== ref) { | ||
t0 = () => { | ||
ref.current = 2; | ||
}; | ||
$[0] = ref; | ||
$[1] = t0; | ||
} else { | ||
t0 = $[1]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now correct!
See test fixture --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31519). * #31521 * __->__ #31519
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome!
if (isRefValueType(maybeDependency.identifier)) { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah interesting. seems worth trying to add an invariant here in a follow-up and see if there's any place it gets hit
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); ```
…31200) Recursively visit inner function instructions to extract dependencies instead of using `LoweredFunction.dependencies` directly. This is currently gated by enableFunctionDependencyRewrite, which needs to be removed before we delete `LoweredFunction.dependencies` altogether (#31204). Some nice side effects - optional-chaining deps for inner functions - full DCE and outlining for inner functions (see #31202) - fewer extraneous instructions (see #31204) - --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31200). * #31202 * #31203 * #31201 * __->__ #31200 * #31521
`JSXMemberExpression` is currently the only instruction (that I know of) that directly references identifier lvalues without a corresponding `LoadLocal`. This has some side effects: - deadcode elimination and constant propagation now reach JSXMemberExpressions - we can delete `LoweredFunction.dependencies` without dangling references (previously, the only reference to JSXMemberExpression objects in HIR was in function dependencies) - JSXMemberExpression now is consistent with all other instructions (e.g. has a rvalue-producing LoadLocal) ' --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31201). * #31202 * #31203 * __->__ #31201 * #31200 * #31521
Add more `FIXTURE_ENTRYPOINT`s ' --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31203). * #31202 * __->__ #31203 * #31201 * #31200 * #31521
Now that we rely on function context exclusively, let's clean up `HIRFunction.context` after DCE. This PR is in preparation of #31204, which would otherwise have unnecessary declarations (of context values that become entirely DCE'd) ' --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31202). * __->__ #31202 * #31203 * #31201 * #31200 * #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 areuseRef
calls. Now we have special handling for this caseStack created with Sapling. Best reviewed with ReviewStack.
dependencies
in propagateScopeDeps #31200