-
Notifications
You must be signed in to change notification settings - Fork 47.7k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
React.pure automatically forwards ref #13822
Conversation
We're not planning to encourage legacy context, and without this change, it's difficult to use pure+forwardRef together. We could special-case `pure(forwardRef(...))` but this is hopefully simpler. ```js React.pure(function(props, ref) { // ... }); ```
Neat. I like this. |
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.
Need to fix this for forwardRef
, too,
@@ -246,7 +247,7 @@ function updatePureComponent( | |||
// Default to shallow comparison | |||
let compare = Component.compare; | |||
compare = compare !== null ? compare : shallowEqual; | |||
if (compare(prevProps, nextProps)) { | |||
if (workInProgress.ref === current.ref && compare(prevProps, nextProps)) { |
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! Need to fix this for forwardRef
, too, but we can do that in a separate PR.
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.
Looks like we do already:
react/packages/react-reconciler/src/ReactFiberBeginWork.js
Lines 187 to 208 in 0af8199
function updateForwardRef( | |
current: Fiber | null, | |
workInProgress: Fiber, | |
type: any, | |
nextProps: any, | |
renderExpirationTime: ExpirationTime, | |
) { | |
const render = type.render; | |
const ref = workInProgress.ref; | |
if (hasLegacyContextChanged()) { | |
// Normally we can bail out on props equality but if context has changed | |
// we don't do the bailout and we have to reuse existing props instead. | |
} else if (workInProgress.memoizedProps === nextProps) { | |
const currentRef = current !== null ? current.ref : null; | |
if (ref === currentRef) { | |
return bailoutOnAlreadyFinishedWork( | |
current, | |
workInProgress, | |
renderExpirationTime, | |
); | |
} | |
} |
though I'm not sure we need to check legacy context there?
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.
Problem is we never hit this code path because of:
react/packages/react-reconciler/src/ReactFiberBeginWork.js
Lines 1209 to 1217 in 0af8199
if (current !== null) { | |
const oldProps = current.memoizedProps; | |
const newProps = workInProgress.pendingProps; | |
if ( | |
oldProps === newProps && | |
!hasLegacyContextChanged() && | |
(updateExpirationTime === NoWork || | |
updateExpirationTime > renderExpirationTime) | |
) { |
Details of bundled changes.Comparing: 0af8199...08e62e4 scheduler
Generated by 🚫 dangerJS |
} | ||
return Promise.resolve(Indirection); | ||
return Promise.resolve(React.forwardRef(Indirection)); |
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.
Ironically had to figure out how to compose these for the sole purpose of testing this change.
I don't think we should take this lightly. It's true that one proposal was to always pass the ref as the second argument for all function components. However, another proposal was to instead not special case it and move it to props. The forwardRefs mechanism has the benefit is that we can treat it as a legacy API that unwraps props. For example, extract it from props and instead pass it as a second arg. This would be a slow operation since it would loop over and clone (just like createElement does today). This PR makes it so that we'd have to keep doing that, not just on the "legacy" non-idiomatic This PR is effectively saying that we've decided to always pass the second argument ref and keep special casing ref. |
Fair enough. I was under the impression that |
Reverts #13822. We're not sure we want to do this.
Reverts #13822. We're not sure we want to do this.
We're not planning to encourage legacy context, and without this change, it's difficult to use pure+forwardRef together. We could special-case `pure(forwardRef(...))` but this is hopefully simpler. ```js React.pure(function(props, ref) { // ... }); ```
Reverts facebook#13822. We're not sure we want to do this.
We're not planning to encourage legacy context, and without this change, it's difficult to use pure+forwardRef together. We could special-case `pure(forwardRef(...))` but this is hopefully simpler. ```js React.pure(function(props, ref) { // ... }); ```
Reverts facebook#13822. We're not sure we want to do this.
We're not planning to encourage legacy context, and without this change, it's difficult to use pure+forwardRef together. We could special-case
pure(forwardRef(...))
but this is hopefully simpler.