Skip to content

Conversation

@kolvian
Copy link
Contributor

@kolvian kolvian commented Nov 6, 2025

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":

  const onSubmit = async (data) => {
    const file = ref.current?.toFile(); // Incorrectly flagged as error
  };

  <form onSubmit={handleSubmit(onSubmit)}>

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:

  • allow-ref-access-in-event-handler-wrapper.tsx - Sync handler test input
  • allow-ref-access-in-event-handler-wrapper.expect.md - Sync handler expected output
  • allow-ref-access-in-async-event-handler-wrapper.tsx - Async handler test input
  • allow-ref-access-in-async-event-handler-wrapper.expect.md - Async handler expected output

All linters and test suites also pass.

@meta-cla meta-cla bot added the CLA Signed label Nov 6, 2025
@react-sizebot
Copy link

react-sizebot commented Nov 6, 2025

The size diff is too large to display in a single comment. The GitHub action for this pull request contains an artifact called 'sizebot-message.md' with the full message.

Generated by 🚫 dangerJS against afef137

@kolvian kolvian force-pushed the allow-ref-in-event-handler branch from 93f0ffe to afef137 Compare November 6, 2025 20:20
Copy link
Member

@josephsavona josephsavona left a 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.

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
@kolvian kolvian force-pushed the allow-ref-in-event-handler branch from afef137 to 320dd89 Compare November 8, 2025 02:43
@kolvian kolvian force-pushed the allow-ref-in-event-handler branch from 320dd89 to 06400ca Compare November 8, 2025 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Getting Cannot access refs during render error from event handler.

3 participants