-
Notifications
You must be signed in to change notification settings - Fork 47.2k
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
Pass ref as normal prop #28348
Pass ref as normal prop #28348
Conversation
20e5daf
to
41c6574
Compare
9f74cab
to
856f9ab
Compare
bd1b316
to
6652d50
Compare
// flag is removed, we should get the ref off the props object right | ||
// before using it. | ||
const refProp = props.ref; | ||
ref = refProp !== undefined ? refProp : null; |
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.
Passing it along here mostly exists for parity with Fizz in terms of file structure. It'll just error lower. We can probably just pass null
here and disable the error.
@@ -698,6 +699,8 @@ function renderElement( | |||
// When the ref moves to the regular props object this will implicitly | |||
// throw for functions. We could probably relax it to a DEV warning for other | |||
// cases. | |||
// TODO: `ref` is now just a prop when `enableRefAsProp` is on. Should we | |||
// do what the above comment says? |
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.
We should really disable the error here when the flag is on and instead error inside the host component path instead, where it can check props.ref
when the flag is on. Class components already error anyway.
When a "ref" prop is passed with a function to a client component it'll just be a general error but we could special case that message similar to how special cased function passed to children here:
props, | ||
_owner: null, | ||
}: any); | ||
Object.defineProperty(element, 'ref', { |
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.
Not sure if it helps but it might be worth keeping these as getters that return null so that the object have the same shape whether it's from Flight or JSX.
); | ||
} | ||
} | ||
return ref; |
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.
Instead of closure per element it might be worth having a hoisted getter that returns this.props.ref
.
return ReactElement( | ||
oldElement.type, | ||
newKey, | ||
oldElement.ref, |
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.
Good thing you caught this with the warning.
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.
Left some comments on minor things.
There's a ton of overlap between the createElement implementation and the JSX implementation, so I combined them into a single module. In the actual build output, the shared code between JSX and createElement will get duplicated anyway, because react/jsx-runtime and react (where createElement livs) are separate, flat build artifacts. So this is more about code organization — with a few key exceptions, the implementations of createElement and jsx are highly coupled.
Adding a flag for this so it can be rolled out incrementally at Meta.
6652d50
to
aa4cec5
Compare
Changes the behavior of the JSX runtime to pass through `ref` as a normal prop, rather than plucking it from the props object and storing on the element. This is a breaking change since it changes the type receiving component. However, most code is unaffected since it's unlikely that a component would have attempted to access a `ref` prop, since it was not possible to get a reference to one. `forwardRef` _will_ still pluck `ref` from the props object, though, since it's extremely common for users to spread the props object onto the inner component and pass `ref` as a differently named prop. This is for maximum compatibility with existing code — the real impact of this change is that `forwardRef` is no longer required. Currently, refs are resolved during child reconciliation and stored on the fiber. As a result of this change, we can move ref resolution to happen only much later, and only for components that actually use them. Then we can remove the `ref` field from the Fiber type. I have not yet done that in this step, though.
aa4cec5
to
e726c0e
Compare
In the last commit I removed the `ref` property from the element type completely. Instead, let's keep it for another release cycle but warn if it's accessed. In dev, we add a non-enumerable getter with `defineProperty` and warn whenever it's invoked. We don't warn on access if a ref is not given. This reduces false positives in cases where a test serializer uses `getOwnPropertyDescriptors`` to compare objects, like Jest does, which is a problem because it bypasses non-enumerability. So unfortunately this will trigger a false positive warning in Jest when the diff is printed: expect(<div ref={ref} />).toEqual(<span ref={ref} />); A bit sketchy, but this is what we've done for the `props.key` and `props.ref` accessors for years, which implies it will be good enough for `element.ref`, too. Let's see if anyone complains.
e726c0e
to
63c9fde
Compare
Depends on: - #28317 - #28320 --- Changes the behavior of the JSX runtime to pass through `ref` as a normal prop, rather than plucking it from the props object and storing on the element. This is a breaking change since it changes the type of the receiving component. However, most code is unaffected since it's unlikely that a component would have attempted to access a `ref` prop, since it was not possible to get a reference to one. `forwardRef` _will_ still pluck `ref` from the props object, though, since it's extremely common for users to spread the props object onto the inner component and pass `ref` as a differently named prop. This is for maximum compatibility with existing code — the real impact of this change is that `forwardRef` is no longer required. Currently, refs are resolved during child reconciliation and stored on the fiber. As a result of this change, we can move ref resolution to happen only much later, and only for components that actually use them. Then we can remove the `ref` field from the Fiber type. I have not yet done that in this step, though. DiffTrain build for [fa2f82a](fa2f82a)
Currently we have code like this const element = Children.only(children);
const combinedRef = useCombinedRefs(ref, element.ref);
const clonedElement = cloneElement(element, { ref: combinedRef }); Will we need to change |
Full list of changes (not a public CHANGELOG): * feature[REMOVED][devtools]: turn off / hide location based component filters ([hoxyq](https://github.com/hoxyq) in [#28417](#28417)) * Add useSyncExternalStore and useTransition to getPrimitiveStackCache ([jamesbvaughan](https://github.com/jamesbvaughan) in [#28399](#28399)) * chore[devtools]: use react-window from npm and bump react-virtualized-auto-sizer to ^1.0.23 ([hoxyq](https://github.com/hoxyq) in [#28408](#28408)) * Pass ref as normal prop ([acdlite](https://github.com/acdlite) in [#28348](#28348)) * Combine createElement and JSX modules ([acdlite](https://github.com/acdlite) in [#28320](#28320)) * [Debug Tools] Always use includeHooksSource option ([sebmarkbage](https://github.com/sebmarkbage) in [#28309](#28309)) * Revert "[Tests] Reset modules by default" ([acdlite](https://github.com/acdlite) in [#28318](#28318)) * Switch <Context> to mean <Context.Provider> ([gaearon](https://github.com/gaearon) in [#28226](#28226)) * [Debug Tools] Introspect Promises in use() ([sebmarkbage](https://github.com/sebmarkbage) in [#28297](#28297)) * fix[devtools/useModalDismissSignal]: use getRootNode for shadow root case support ([hoxyq](https://github.com/hoxyq) in [#28145](#28145)) * fix: define IS_ACT_ENVIRONMENT global for tests with concurrent mode and synchronous act ([hoxyq](https://github.com/hoxyq) in [#28296](#28296)) * chore: gate legacy apis for react-devtools-shell ([hoxyq](https://github.com/hoxyq) in [#28273](#28273)) * DevTools: Add support for use(Context) ([eps1lon](https://github.com/eps1lon) in [#28233](#28233)) * Remove __self and __source location from elements ([sebmarkbage](https://github.com/sebmarkbage) in [#28265](#28265)) * chore: use versioned render in inspectedElement test ([hoxyq](https://github.com/hoxyq) in [#28246](#28246)) * chore: use versioned render in TimelineProfiler test and gate some for legacy rendering ([hoxyq](https://github.com/hoxyq) in [#28218](#28218)) * [Tests] Reset modules by default ([rickhanlonii](https://github.com/rickhanlonii) in [#28254](#28254)) * chore: use versioned render in preprocessData test and gate some for … ([hoxyq](https://github.com/hoxyq) in [#28219](#28219)) * chore: use versioned render in storeStressSync test and gate them for legacy rendering ([hoxyq](https://github.com/hoxyq) in [#28216](#28216)) * Patch devtools before running useMemo function in strict mode ([gsathya](https://github.com/gsathya) in [#28249](#28249)) * chore: use versioned render in storeComponentFilters test ([hoxyq](https://github.com/hoxyq) in [#28241](#28241)) * chore: use versioned render in profilerContext test ([hoxyq](https://github.com/hoxyq) in [#28243](#28243)) * chore: use versioned render in profilingCommitTreeBuilder test and gate some for legacy rendering ([hoxyq](https://github.com/hoxyq) in [#28236](#28236)) * chore: use versioned render in profilingHostRoot test and gate some for legacy rendering ([hoxyq](https://github.com/hoxyq) in [#28237](#28237)) * chore: use versioned render in profilingCache test ([hoxyq](https://github.com/hoxyq) in [#28242](#28242)) * chore: use versioned render in ownersListContext test ([hoxyq](https://github.com/hoxyq) in [#28240](#28240)) * chore: use versioned render in editing test ([hoxyq](https://github.com/hoxyq) in [#28239](#28239)) * chore: use versioned render in treeContext test ([hoxyq](https://github.com/hoxyq) in [#28245](#28245)) * chore: use versioned render in store test ([hoxyq](https://github.com/hoxyq) in [#28244](#28244)) * chore: use versioned render in profilerStore test ([hoxyq](https://github.com/hoxyq) in [#28238](#28238)) * chore: use versioned render in profilingCharts test ([hoxyq](https://github.com/hoxyq) in [#28235](#28235)) * chore: use versioned render in profilerChangeDescriptions test ([hoxyq](https://github.com/hoxyq) in [#28221](#28221)) * chore: use versioned render in storeOwners test ([hoxyq](https://github.com/hoxyq) in [#28215](#28215)) * chore: use versioned render in componentStacks test ([hoxyq](https://github.com/hoxyq) in [#28214](#28214)) * chore: use versioned render in console test ([hoxyq](https://github.com/hoxyq) in [#28213](#28213)) * chore: use versioned render in useEditableValue test ([hoxyq](https://github.com/hoxyq) in [#28212](#28212)) * chore: use versioned render in FastRefreshDevToolsIntegration test ([hoxyq](https://github.com/hoxyq) in [#28211](#28211)) * chore: add versioned render implementation for DevTools tests ([hoxyq](https://github.com/hoxyq) in [#28210](#28210)) * chore: add single versioned implementation of act for DevTools tests ([hoxyq](https://github.com/hoxyq) in [#28186](#28186)) * DevTools: Add support for useFormState ([eps1lon](https://github.com/eps1lon) in [#28232](#28232)) * DevTools: Add support for useOptimistic Hook ([eps1lon](https://github.com/eps1lon) in [#27982](#27982)) * Add stable React.act export ([acdlite](https://github.com/acdlite) in [#28160](#28160)) * [flow] upgrade to 0.225.1 ([kassens](https://github.com/kassens) in [#27871](#27871)) * fix[devtools/e2e]: add fallback for act in integration tests ([hoxyq](https://github.com/hoxyq) in [#27842](#27842)) * Add stable concurrent option to react-test-renderer ([jackpope](https://github.com/jackpope) in [#27804](#27804)) * Update act references in tests ([gnoff](https://github.com/gnoff) in [#27805](#27805)) * Flow: make more objects exact ([kassens](https://github.com/kassens) in [#27790](#27790))
### React upstream changes - facebook/react#28438 - facebook/react#28436 - facebook/react#25954 - facebook/react#28434 - facebook/react#28433 - facebook/react#28432 - facebook/react#28415 - facebook/react#27903 - facebook/react#28430 - facebook/react#28424 - facebook/react#28400 - facebook/react#28422 - facebook/react#28423 - facebook/react#28412 - facebook/react#28418 - facebook/react#28421 - facebook/react#28417 - facebook/react#28399 - facebook/react#28408 - facebook/react#28350 - facebook/react#28387 - facebook/react#28403 - facebook/react#28384 - facebook/react#28409 - facebook/react#28398 - facebook/react#28405 - facebook/react#28328 - facebook/react#28402 - facebook/react#28386 - facebook/react#28388 - facebook/react#28379 - facebook/react#28383 - facebook/react#28390 - facebook/react#28389 - facebook/react#28382 - facebook/react#28348 Closes NEXT-2600
Depends on: - facebook#28317 - facebook#28320 --- Changes the behavior of the JSX runtime to pass through `ref` as a normal prop, rather than plucking it from the props object and storing on the element. This is a breaking change since it changes the type of the receiving component. However, most code is unaffected since it's unlikely that a component would have attempted to access a `ref` prop, since it was not possible to get a reference to one. `forwardRef` _will_ still pluck `ref` from the props object, though, since it's extremely common for users to spread the props object onto the inner component and pass `ref` as a differently named prop. This is for maximum compatibility with existing code — the real impact of this change is that `forwardRef` is no longer required. Currently, refs are resolved during child reconciliation and stored on the fiber. As a result of this change, we can move ref resolution to happen only much later, and only for components that actually use them. Then we can remove the `ref` field from the Fiber type. I have not yet done that in this step, though.
Full list of changes (not a public CHANGELOG): * feature[REMOVED][devtools]: turn off / hide location based component filters ([hoxyq](https://github.com/hoxyq) in [facebook#28417](facebook#28417)) * Add useSyncExternalStore and useTransition to getPrimitiveStackCache ([jamesbvaughan](https://github.com/jamesbvaughan) in [facebook#28399](facebook#28399)) * chore[devtools]: use react-window from npm and bump react-virtualized-auto-sizer to ^1.0.23 ([hoxyq](https://github.com/hoxyq) in [facebook#28408](facebook#28408)) * Pass ref as normal prop ([acdlite](https://github.com/acdlite) in [facebook#28348](facebook#28348)) * Combine createElement and JSX modules ([acdlite](https://github.com/acdlite) in [facebook#28320](facebook#28320)) * [Debug Tools] Always use includeHooksSource option ([sebmarkbage](https://github.com/sebmarkbage) in [facebook#28309](facebook#28309)) * Revert "[Tests] Reset modules by default" ([acdlite](https://github.com/acdlite) in [facebook#28318](facebook#28318)) * Switch <Context> to mean <Context.Provider> ([gaearon](https://github.com/gaearon) in [facebook#28226](facebook#28226)) * [Debug Tools] Introspect Promises in use() ([sebmarkbage](https://github.com/sebmarkbage) in [facebook#28297](facebook#28297)) * fix[devtools/useModalDismissSignal]: use getRootNode for shadow root case support ([hoxyq](https://github.com/hoxyq) in [facebook#28145](facebook#28145)) * fix: define IS_ACT_ENVIRONMENT global for tests with concurrent mode and synchronous act ([hoxyq](https://github.com/hoxyq) in [facebook#28296](facebook#28296)) * chore: gate legacy apis for react-devtools-shell ([hoxyq](https://github.com/hoxyq) in [facebook#28273](facebook#28273)) * DevTools: Add support for use(Context) ([eps1lon](https://github.com/eps1lon) in [facebook#28233](facebook#28233)) * Remove __self and __source location from elements ([sebmarkbage](https://github.com/sebmarkbage) in [facebook#28265](facebook#28265)) * chore: use versioned render in inspectedElement test ([hoxyq](https://github.com/hoxyq) in [facebook#28246](facebook#28246)) * chore: use versioned render in TimelineProfiler test and gate some for legacy rendering ([hoxyq](https://github.com/hoxyq) in [facebook#28218](facebook#28218)) * [Tests] Reset modules by default ([rickhanlonii](https://github.com/rickhanlonii) in [facebook#28254](facebook#28254)) * chore: use versioned render in preprocessData test and gate some for … ([hoxyq](https://github.com/hoxyq) in [facebook#28219](facebook#28219)) * chore: use versioned render in storeStressSync test and gate them for legacy rendering ([hoxyq](https://github.com/hoxyq) in [facebook#28216](facebook#28216)) * Patch devtools before running useMemo function in strict mode ([gsathya](https://github.com/gsathya) in [facebook#28249](facebook#28249)) * chore: use versioned render in storeComponentFilters test ([hoxyq](https://github.com/hoxyq) in [facebook#28241](facebook#28241)) * chore: use versioned render in profilerContext test ([hoxyq](https://github.com/hoxyq) in [facebook#28243](facebook#28243)) * chore: use versioned render in profilingCommitTreeBuilder test and gate some for legacy rendering ([hoxyq](https://github.com/hoxyq) in [facebook#28236](facebook#28236)) * chore: use versioned render in profilingHostRoot test and gate some for legacy rendering ([hoxyq](https://github.com/hoxyq) in [facebook#28237](facebook#28237)) * chore: use versioned render in profilingCache test ([hoxyq](https://github.com/hoxyq) in [facebook#28242](facebook#28242)) * chore: use versioned render in ownersListContext test ([hoxyq](https://github.com/hoxyq) in [facebook#28240](facebook#28240)) * chore: use versioned render in editing test ([hoxyq](https://github.com/hoxyq) in [facebook#28239](facebook#28239)) * chore: use versioned render in treeContext test ([hoxyq](https://github.com/hoxyq) in [facebook#28245](facebook#28245)) * chore: use versioned render in store test ([hoxyq](https://github.com/hoxyq) in [facebook#28244](facebook#28244)) * chore: use versioned render in profilerStore test ([hoxyq](https://github.com/hoxyq) in [facebook#28238](facebook#28238)) * chore: use versioned render in profilingCharts test ([hoxyq](https://github.com/hoxyq) in [facebook#28235](facebook#28235)) * chore: use versioned render in profilerChangeDescriptions test ([hoxyq](https://github.com/hoxyq) in [facebook#28221](facebook#28221)) * chore: use versioned render in storeOwners test ([hoxyq](https://github.com/hoxyq) in [facebook#28215](facebook#28215)) * chore: use versioned render in componentStacks test ([hoxyq](https://github.com/hoxyq) in [facebook#28214](facebook#28214)) * chore: use versioned render in console test ([hoxyq](https://github.com/hoxyq) in [facebook#28213](facebook#28213)) * chore: use versioned render in useEditableValue test ([hoxyq](https://github.com/hoxyq) in [facebook#28212](facebook#28212)) * chore: use versioned render in FastRefreshDevToolsIntegration test ([hoxyq](https://github.com/hoxyq) in [facebook#28211](facebook#28211)) * chore: add versioned render implementation for DevTools tests ([hoxyq](https://github.com/hoxyq) in [facebook#28210](facebook#28210)) * chore: add single versioned implementation of act for DevTools tests ([hoxyq](https://github.com/hoxyq) in [facebook#28186](facebook#28186)) * DevTools: Add support for useFormState ([eps1lon](https://github.com/eps1lon) in [facebook#28232](facebook#28232)) * DevTools: Add support for useOptimistic Hook ([eps1lon](https://github.com/eps1lon) in [facebook#27982](facebook#27982)) * Add stable React.act export ([acdlite](https://github.com/acdlite) in [facebook#28160](facebook#28160)) * [flow] upgrade to 0.225.1 ([kassens](https://github.com/kassens) in [facebook#27871](facebook#27871)) * fix[devtools/e2e]: add fallback for act in integration tests ([hoxyq](https://github.com/hoxyq) in [facebook#27842](facebook#27842)) * Add stable concurrent option to react-test-renderer ([jackpope](https://github.com/jackpope) in [facebook#27804](facebook#27804)) * Update act references in tests ([gnoff](https://github.com/gnoff) in [facebook#27805](facebook#27805)) * Flow: make more objects exact ([kassens](https://github.com/kassens) in [facebook#27790](facebook#27790))
Depends on: - #28317 - #28320 --- Changes the behavior of the JSX runtime to pass through `ref` as a normal prop, rather than plucking it from the props object and storing on the element. This is a breaking change since it changes the type of the receiving component. However, most code is unaffected since it's unlikely that a component would have attempted to access a `ref` prop, since it was not possible to get a reference to one. `forwardRef` _will_ still pluck `ref` from the props object, though, since it's extremely common for users to spread the props object onto the inner component and pass `ref` as a differently named prop. This is for maximum compatibility with existing code — the real impact of this change is that `forwardRef` is no longer required. Currently, refs are resolved during child reconciliation and stored on the fiber. As a result of this change, we can move ref resolution to happen only much later, and only for components that actually use them. Then we can remove the `ref` field from the Fiber type. I have not yet done that in this step, though. DiffTrain build for commit fa2f82a.
Depends on:
Changes the behavior of the JSX runtime to pass through
ref
as a normal prop, rather than plucking it from the props object and storing on the element.This is a breaking change since it changes the type of the receiving component. However, most code is unaffected since it's unlikely that a component would have attempted to access a
ref
prop, since it was not possible to get a reference to one.forwardRef
will still pluckref
from the props object, though, since it's extremely common for users to spread the props object onto the inner component and passref
as a differently named prop. This is for maximum compatibility with existing code — the real impact of this change is thatforwardRef
is no longer required.Currently, refs are resolved during child reconciliation and stored on the fiber. As a result of this change, we can move ref resolution to happen only much later, and only for components that actually use them. Then we can remove the
ref
field from the Fiber type. I have not yet done that in this step, though.