Skip to content

Commit

Permalink
Changes in response to Sophie's feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
bvaughn committed Mar 27, 2018
1 parent 712f4de commit 4610392
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 20 deletions.
18 changes: 9 additions & 9 deletions content/blog/2018-03-27-update-on-async-rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ One of the biggest lessons we've learned is that some of our legacy component li
* `componentWillReceiveProps`
* `componentWillUpdate`

These lifecycle methods have often been misunderstood and subtly misused; furthermore, we anticipate that their potential misuse may be more problematic with async rendering. Because of this, we will be adding an "UNSAFE_" prefix to these lifecycles in an upcoming release.
These lifecycle methods have often been misunderstood and subtly misused; furthermore, we anticipate that their potential misuse may be more problematic with async rendering. Because of this, we will be adding an "UNSAFE_" prefix to these lifecycles in an upcoming release. (Here, "unsafe" refers not to security but instead conveys that code using these lifecycles will be more likely to have bugs in future versions of React, especially once async rendering is enabled.)

## Gradual Migration Path

[React follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so this change will be gradual. Our current plan is:

* **16.3**: Introduce aliases for the unsafe lifecycles, `UNSAFE_componentWillMount`, `UNSAFE_componentWillReceiveProps`, and `UNSAFE_componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.)
* **16.x**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release, but the old names will log a DEV-mode warning.)
* **A future 16.x release**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release, but the old names will log a DEV-mode warning.)
* **17.0**: Remove `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate` . (Only the new "UNSAFE_" lifecycle names will work from this point forward.)

**Note that if you're a React application developer, you don't have to do anything about the legacy methods yet. The primary purpose of the upcoming version 16.3 release is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will not be enabled until a future 16.x release.**
Expand All @@ -45,16 +45,12 @@ Together with `componentDidUpdate`, this new lifecycle should cover all use case

### New lifecycle: `getSnapshotBeforeUpdate`

The new `getSnapshotBeforeUpdate` lifecycle is called right before mutations are made (e.g. before the DOM is updated). The return value for this lifecycle will be passed as the third parameter to `componentDidUpdate`.
The new `getSnapshotBeforeUpdate` lifecycle is called right before mutations are made (e.g. before the DOM is updated). The return value for this lifecycle will be passed as the third parameter to `componentDidUpdate`. (This lifecycle isn't often needed, but can be useful in cases like manually preserving scroll position during rerenders.)

Together with `componentDidUpdate`, this new lifecycle should cover all use cases for the legacy `componentWillUpdate`.

We'll look at examples of how both of these lifecycles can be used below.

> Note
>
> For brevity, the examples below are written using the experimental class properties transform, but the same migration strategies apply without it.
## Examples
- [Initializing state](#initializing-state)
- [Fetching external data](#fetching-external-data)
Expand All @@ -64,6 +60,10 @@ We'll look at examples of how both of these lifecycles can be used below.
- [Updating external data when props change](#updating-external-data-when-props-change)
- [Reading DOM properties before an update](#reading-dom-properties-before-an-update)

> Note
>
> For brevity, the examples below are written using the experimental class properties transform, but the same migration strategies apply without it.
### Initializing state

This example shows a component with `setState` calls inside of `componentWillMount`:
Expand Down Expand Up @@ -102,7 +102,7 @@ People often assume that `componentWillMount` and `componentWillUnmount` are alw
For this reason, the recommended way to add listeners/subscriptions is to use the `componentDidMount` lifecycle:
`embed:update-on-async-rendering/adding-event-listeners-after.js`

Sometimes it is important to update subscriptions in response to property changes. If you're using a library like Redux or MobX, the library's container component should handle this for you. For application authors, we've created a small library, [`create-subscription`](https://github.com/facebook/react/tree/master/packages/create-subscription), to help with this.
Sometimes it is important to update subscriptions in response to property changes. If you're using a library like Redux or MobX, the library's container component should handle this for you. For application authors, we've created a small library, [`create-subscription`](https://github.com/facebook/react/tree/master/packages/create-subscription), to help with this. We'll publish it along with React 16.3.

Rather than passing a subscribable `dataSource` prop as we did in the example above, we could use `create-subscription` to pass in the subscribed value:

Expand Down Expand Up @@ -171,7 +171,7 @@ Open source maintainers might be wondering what these changes mean for shared co

Fortunately, you do not!

In support of version 16.3, we've also created a new NPM package, [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat). This package polyfills components so that the new `getDerivedStateFromProps` lifecycle will also work with older versions of React (0.14.9+).
When React 16.3 is published, we'll also publish a new npm package, [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat). This package polyfills components so that the new `getDerivedStateFromProps` lifecycle will also work with older versions of React (0.14.9+).

To use this polyfill, first add it as a dependency to your library:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {createSubscription} from 'create-subscription';
const Subscription = createSubscription({
getCurrentValue(sourceProp) {
// Return the current value of the subscription (sourceProp).
// highlight-next-line
return sourceProp.value;
},

Expand All @@ -14,11 +13,9 @@ const Subscription = createSubscription({

// Subscribe (e.g. add an event listener) to the subscription (sourceProp).
// Call callback(newValue) whenever a subscription changes.
// highlight-next-line
sourceProp.subscribe(handleSubscriptionChange);

// Return an unsubscribe method.
// highlight-range{1-3}
return function unsubscribe() {
sourceProp.unsubscribe(handleSubscriptionChange);
};
Expand All @@ -27,7 +24,6 @@ const Subscription = createSubscription({

// Rather than passing the subscribable source to our ExampleComponent,
// We could just pass the subscribed value directly:
// highlight-range{1-3}
<Subscription source={dataSource}>
{value => <ExampleComponent subscribedValue={value} />}
</Subscription>;
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ScrollingList extends React.Component {
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
this.listRef.scrollTop +=
this.listRef.scrollHeight - snapshot;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
class ScrollingList extends React.Component {
listRef = null;
prevScrollHeight = null;
previousScrollHeight = null;

// highlight-range{1-7}
componentWillUpdate(nextProps, nextState) {
// Are we adding new items to the list?
// Capture the current height of the list so we can adjust scroll later.
if (this.props.list.length < nextProps.list.length) {
this.prevScrollHeight = this.listRef.scrollHeight;
this.previousScrollHeight = this.listRef.scrollHeight;
}
}

// highlight-range{1-9}
// highlight-range{1-10}
componentDidUpdate(prevProps, prevState) {
// If prevScrollHeight is set, we've just added new items.
// If previousScrollHeight is set, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
if (this.prevScrollHeight !== null) {
if (this.previousScrollHeight !== null) {
this.listRef.scrollTop +=
this.listRef.scrollHeight - this.prevScrollHeight;
this.prevScrollHeight = null;
this.listRef.scrollHeight -
this.previousScrollHeight;
this.previousScrollHeight = null;
}
}

Expand Down

0 comments on commit 4610392

Please sign in to comment.