-
Notifications
You must be signed in to change notification settings - Fork 558
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
New commit phase lifecycle getSnapshotBeforeUpdate() #33
Conversation
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.
How is this conceptually different from componentWillUpdate and componentWillMount? The similarity in the names makes me wonder if it makes sense to not deprecate the old methods and just reuse them in a semver major change.
componentWillUpdate and componentWillMount are called before render, and may be called for a render that is discarded because of a higher priority update. The newly proposed lifecycles would be called during the "commit" phase. This draft docs page does a nice job in explaining the difference and why it matters to distinguish between the two: https://deploy-preview-587--reactjs.netlify.com/docs/strict-mode.html#detecting-unexpected-side-effects
Reusing the old lifecycles would not be a good option for a couple of reasons. We currently plan to support the old lifecycle functionality in future releases, with an "UNSAFE_" prefix. Having similarly named methods with significantly different behavior would cause confusion and could make updating from 16 to 17 significantly more difficult. We will also want these new lifecycles to be available in a minor release of 16- so people can begin upgrading to use async. For this reason as well the names must be unique. Some familiarity is probably helpful though, since people already have a concept of mounting and updating. |
Specifically an important distinction is that people expect to be able to use |
I do wish we could recycle the existing names though since "will mount" and "will update" indeed seem like ideal names for these APIs 😢 |
I guess we can add them under new names and then eventually find better names for them when/if we ever introduce a new lifecycle API. |
Yeah I'm definitely open for bike-shedding on better names than the ones I proposed initially though. 😄 |
I like the idea of Could we even perhaps just have a single event for both cases of mounting and updating? I'm curious if the extra API surface is necessary in practice. |
I don't think a single lifecycle is a good idea, because |
Maybe
|
I don't object too strongly to that, but I did intentionally avoid using "will" or "did" in the name because of the potential to cause confusion. With the (kind of awkward) caveat of |
|
|
|
Renamed |
Can |
Briefly considered making it static but decided against it because of refs. Felt odd to force refs to be stored in state. |
What about |
I'm personally not a fan of the "componentWill" prefix for this use-case because (with the exception of |
One potential use case is in Draft.js, where we have two values we track that are outside of state and get updated roughly at the time this instance method would be called. The current, short-term work-around we are doing is to update them in the I think this could be a possible solution for the Draft.js use case, @sophiebits would be curious what you think when you are online again. I also think that for Draft.js we also will not need the 'before-mount' call. Seems like Draft wasn't setting those flags in 'willMount'. To be more specific about what Draft does by tracking those values - In one case it is intended to block stray 'select' events that fire in IE after Draft has already started a selection update, and in the other case it's caching the latest rendered state, so for both of those I don't think the initial mount is a problem. |
Reparenting (RFC #28) could make use of this feature. If these pieces of state are really important to a user, they could be snapshotted before reparenting using this RFC and restored after they are moved to a new dom node. |
@flarnie For Draft I don't think we need any new lifecycles, instead maybe just we should refactor our componentDidMount calls so that we don't need to depend on the ordering as strongly. (That likely looks like the parent component controlling the selection handling instead of the leaves.) |
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.
Should we also mention about shouldComponentUpdate
and how will its return value true
or false
will impact this?
|
||
# Motivation | ||
|
||
This lifecycle provides a way for asynchronously rendered components to accurately read values from the host envirnment (e.g. the DOM) before it is mutated. |
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.
envirnment
-> environment
|
||
### No return value | ||
|
||
The proposed lifecycle will be the first commit phase lifecycle with a meaningful return value and the first lifecyle whose return value is passed as a parameter to another lifecycle. Likewise, the new parameter for `componentDidUpdate` will be the first passed to a lifecycle that isn't some form of `Props` or `State`. This adds some complexity to the API, since it requires a more nuanced understanding the relationship between `getSnapshotBeforeUpdate` and `componentDidUpdate`. |
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.
lifecyle
-> lifecycle
|
||
The proposed lifecycle will be the first commit phase lifecycle with a meaningful return value and the first lifecyle whose return value is passed as a parameter to another lifecycle. Likewise, the new parameter for `componentDidUpdate` will be the first passed to a lifecycle that isn't some form of `Props` or `State`. This adds some complexity to the API, since it requires a more nuanced understanding the relationship between `getSnapshotBeforeUpdate` and `componentDidUpdate`. | ||
|
||
An alternative would be to scrap the return value in favor of storing snapshot values on the instance. |
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.
favor
-> favour
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.
Both are valid spellings.
This lifecycle will be invoked if |
This RFC has entered the final comment period. |
d08f57f
to
ec7ce99
Compare
I've created a potential polyfill for this method with reactjs/react-lifecycles-compat/pull/1 but I don't think it's worth merging because of the reasons mentioned in the RFC. |
Can you expand on what
means? |
Sure, sure. I meant cases like this: class Test {
constructor() {
Object.defineProperty(this, "componentDidUpdate", {
configurable: true,
enumerable: true,
writable: true,
value: (prevProps, prevState) => {// ...
}
});
}
} Which is what Babel would generate for: class Test {
componentDidUpdate = (prevProps, prevState) => {
// ...
}
} I could probably have worded this better. |
I think I'd be fine with not supporting that pattern. It shouldn't ever be necessary. As long as we document that in the polyfill README. |
Gotcha. It is at least possible to detect and error in this case (when cDU isn't on the prototype) which the polyfill POC PR does. |
Thanks for the suggestions and discussion, everyone. |
View formatted RFC