-
Notifications
You must be signed in to change notification settings - Fork 49.8k
[compiler] Allow ref access in callbacks passed to event handler props #35062
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
base: main
Are you sure you want to change the base?
Conversation
93f0ffe to
afef137
Compare
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.
Thanks for the PR! The approach here works in some cases, but it's encoding some important information — which values can be inferred as event handlers — in an-hoc way. The way we would typically handle this is by adding types to represent a concept, inferring the types appropriately in InferTypes, and then using the type information in later passes.
So the way to go here would be to add a BuiltinEventHandlerId shapeId (see ObjectShape.ts with all the BuiltIn...Id constants), then update InferTypes to infer the types of JsxExpression props as {kind: 'Function', shapeId: BuiltinEventHandlerId, ...} using similar logic to what you have here. Ie, for primitive tags where the prop is a named prop starting with "on".
Then, ValidateNoRefAccessInRender can allow ref-accessing functions to flow into expressions if the result is known to be an event handler. Then you can add an extra case into the logic at https://github.com/facebook/react/blob/main/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts#L521C21-L553, along the lines of
const isRefLValue = isUseRefType(instr.lvalue.identifier);
+ const isEventHandlerLValue = isEventHandlerType(instr.lvalue.identifier);
...
if (
isRevLValue ||
+ isEventHandlerLValue ||
...Finally, all of this should only be enabled behind a feature flag, which you can add in Environment.ts: enableInferEventHandlers, false by default. Put the logic in InferTypes behind this feature flag (the ValidateNoRefAccess changes will only take effect if the type is inferred, which it won't be w/o the feature).
Make sure to add // @ enableInferEventHandlers as the first line of each of your new test fixtures to enable the feature.
packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js
Outdated
Show resolved
Hide resolved
Fixes a false positive where the compiler incorrectly flags ref access
in callbacks passed to event handler wrappers on built-in DOM elements.
For example, with react-hook-form:
<form onSubmit={handleSubmit(onSubmit)}>
Where onSubmit accesses ref.current, this was incorrectly flagged as
"Cannot access refs during render". This is a false positive because
built-in DOM event handlers are guaranteed by React to only execute in
response to actual events, never during render.
This fix only relaxes validation for built-in DOM elements (not custom
components) to maintain safety. Custom components could call their onFoo
props during render, which would violate ref access rules.
Adds four test fixtures demonstrating allowed and disallowed patterns.
Issue: facebook#35040
afef137 to
320dd89
Compare
320dd89 to
06400ca
Compare
Summary
Fixes #35040. The React compiler incorrectly flags ref access within event handlers as ref access at render time. For example, this code would fail to compile with error "Cannot access refs during render":
This is a false positive because any built-in DOM event handler is guaranteed not to run at render time. This PR only supports built-in event handlers because there are no guarantees that user-made event handlers will not run at render time.
How did you test this change?
I created 4 test fixtures which validate this change:
All linters and test suites also pass.