Skip to content
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

React.pure automatically forwards ref #13822

Merged
merged 1 commit into from
Oct 11, 2018

Conversation

sophiebits
Copy link
Contributor

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.

React.pure(function(props, ref) {
  // ...
});

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) {
  // ...
});
```
@gaearon
Copy link
Collaborator

gaearon commented Oct 11, 2018

Neat. I like this.

Copy link
Collaborator

@acdlite acdlite left a 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)) {
Copy link
Collaborator

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.

Copy link
Contributor Author

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:

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?

Copy link
Collaborator

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:

if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
oldProps === newProps &&
!hasLegacyContextChanged() &&
(updateExpirationTime === NoWork ||
updateExpirationTime > renderExpirationTime)
) {

@sizebot
Copy link

sizebot commented Oct 11, 2018

Details of bundled changes.

Comparing: 0af8199...08e62e4

scheduler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
scheduler.development.js n/a n/a 0 B 19.17 KB 0 B 5.74 KB UMD_DEV
scheduler.production.min.js n/a n/a 0 B 3.16 KB 0 B 1.53 KB UMD_PROD

Generated by 🚫 dangerJS

}
return Promise.resolve(Indirection);
return Promise.resolve(React.forwardRef(Indirection));
Copy link
Contributor Author

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.

@sebmarkbage
Copy link
Collaborator

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 forwardRef API but also for the best-practice pure API too.

This PR is effectively saying that we've decided to always pass the second argument ref and keep special casing ref.

@sophiebits
Copy link
Contributor Author

Fair enough. I was under the impression that ref as the second arg being the best practice had been decided when I was out.

sophiebits added a commit that referenced this pull request Oct 19, 2018
Reverts #13822. We're not sure we want to do this.
sophiebits added a commit that referenced this pull request Oct 19, 2018
Reverts #13822. We're not sure we want to do this.
linjiajian999 pushed a commit to linjiajian999/react that referenced this pull request Oct 22, 2018
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) {
  // ...
});
```
linjiajian999 pushed a commit to linjiajian999/react that referenced this pull request Oct 22, 2018
jetoneza pushed a commit to jetoneza/react that referenced this pull request Jan 23, 2019
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) {
  // ...
});
```
jetoneza pushed a commit to jetoneza/react that referenced this pull request Jan 23, 2019
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.

6 participants