Browse Source

Added getSnapshotBeforeUpdate recipe

main
Brian Vaughn 7 years ago
parent
commit
b3bf0bd520
  1. 30
      content/blog/2018-03-15-update-on-async-rendering.md
  2. 19
      examples/update-on-async-rendering/new-lifecycles-overview.js
  3. 35
      examples/update-on-async-rendering/react-dom-properties-before-update-after.js
  4. 36
      examples/update-on-async-rendering/react-dom-properties-before-update-before.js

30
content/blog/2018-03-15-update-on-async-rendering.md

@ -29,27 +29,24 @@ However, if you'd like to start using the new component API (or if you're a main
--- ---
Before we begin, here's a quick reminder of the lifecycle changes in version 16.3: Before we begin, here's a quick overview of the lifecycle changes planned for version 16.3:
* We are adding the following lifecycle aliases: `UNSAFE_componentWillMount`, `UNSAFE_componentWillReceiveProps`, and `UNSAFE_componentWillUpdate`. (Both the old lifecycle names and the new aliases will be supported.) * We are adding the following lifecycle aliases: `UNSAFE_componentWillMount`, `UNSAFE_componentWillReceiveProps`, and `UNSAFE_componentWillUpdate`. (Both the old lifecycle names and the new aliases will be supported.)
* We are introducing a new, static lifecycle, `getDerivedStateFromProps`: * We are introducing two new lifecycles, static `getDerivedStateFromProps` and `getSnapshotBeforeUpdate`:
```js `embed:update-on-async-rendering/new-lifecycles-overview.js`
static getDerivedStateFromProps(
nextProps: Props,
prevState: State
): $Shape<State> | null
```
This new lifecycle is invoked after a component is instantiated and when it receives new props. It should return an object to update `state`, or `null` to indicate that the new `props` do not require any `state` updates. The new static `getSnapshotBeforeUpdate` lifecycle is invoked after a component is instantiated as well as when it receives new props. It should return an object to update `state`, or `null` to indicate that the new `props` do not require any `state` updates.
--- The new `getSnapshotBeforeUpdate` lifecycle gets 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`.
Now let's take a look at some examples. We'll look at examples of how both of these lifecycles can be used below.
> Note > Note
> >
> For brevity, the examples below are written using the experimental class properties transform, but the same migration strategies apply without it. > For brevity, the examples below are written using the experimental class properties transform, but the same migration strategies apply without it.
---
### Initializing state ### Initializing state
This example shows a component with `setState` calls inside of `componentWillMount`: This example shows a component with `setState` calls inside of `componentWillMount`:
@ -130,6 +127,17 @@ The recommended upgrade path for this component is to move data-updates into `co
> >
> If you're using an HTTP library that supports cancellation, like [axios](https://www.npmjs.com/package/axios), then it's simple to cancel an in-progress request when unmounting. For native Promises, you can use an approach like [the one shown here](https://gist.github.com/bvaughn/982ab689a41097237f6e9860db7ca8d6). > If you're using an HTTP library that supports cancellation, like [axios](https://www.npmjs.com/package/axios), then it's simple to cancel an in-progress request when unmounting. For native Promises, you can use an approach like [the one shown here](https://gist.github.com/bvaughn/982ab689a41097237f6e9860db7ca8d6).
### Reading DOM properties before an update
Here is an example of a component that reads a property from the DOM before an update in order to maintain scroll position within a list:
`embed:update-on-async-rendering/react-dom-properties-before-update-before.js`
In the above example, `componentWillUpdate` is used to read the DOM property. However with async rendering, there may be delays between "render" phase lifecycles (like `componentWillUpdate` and `render`) and "commit" phase lifecycles (like `componentDidUpdate`). A user might continue scrolling during the delay, in which case the position value read from `componentWillUpdate` will be stale.
The solution to this problem is to use the new "commit" phase lifecycle, `getSnapshotBeforeUpdate`. This method gets called _immediately before_ mutations are made (e.g. before the DOM is updated). It can return a value for React to pass as a parameter to `componentDidUpdate`, which gets called _immediately after_ mutations.
`embed:update-on-async-rendering/react-dom-properties-before-update-after.js`
## Other scenarios ## Other scenarios
While we tried to cover the most common use cases in this post, we recognize that we might have missed some of them. If you are using `componentWillMount`, `componentWillUpdate`, or `componentWillReceiveProps` in ways that aren't covered by this blog post, and aren't sure how to migrate off these legacy lifecycles, please [file a new issue against our documentation](https://github.com/reactjs/reactjs.org/issues/new) with your code examples and as much background information as you can provide. We will update this document with new alternative patterns as they come up. While we tried to cover the most common use cases in this post, we recognize that we might have missed some of them. If you are using `componentWillMount`, `componentWillUpdate`, or `componentWillReceiveProps` in ways that aren't covered by this blog post, and aren't sure how to migrate off these legacy lifecycles, please [file a new issue against our documentation](https://github.com/reactjs/reactjs.org/issues/new) with your code examples and as much background information as you can provide. We will update this document with new alternative patterns as they come up.

19
examples/update-on-async-rendering/new-lifecycles-overview.js

@ -0,0 +1,19 @@
class Example extends React.Component<
Props,
State,
Snapshot
> {
static getDerivedStateFromProps(
nextProps: Props,
prevState: State
): $Shape<State> | null {
// ...
}
getSnapshotBeforeUpdate(
prevProps: Props,
prevState: State
): Snapshot {
// ...
}
}

35
examples/update-on-async-rendering/react-dom-properties-before-update-after.js

@ -0,0 +1,35 @@
class ScrollingList extends React.Component {
listRef = null;
// highlight-range{1-8}
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the current height of the list so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
return this.listRef.scrollHeight;
}
return null;
}
// highlight-range{1-8}
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.
if (snapshot !== null) {
this.listRef.scrollTop +=
this.listRef.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.setListRef}>
{/* ...contents... */}
</div>
);
}
setListRef = ref => {
this.listRef = ref;
};
}

36
examples/update-on-async-rendering/react-dom-properties-before-update-before.js

@ -0,0 +1,36 @@
class ScrollingList extends React.Component {
listRef = null;
prevScrollHeight = 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;
}
}
// highlight-range{1-9}
componentDidUpdate(prevProps, prevState) {
// If prevScrollHeight 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) {
this.listRef.scrollTop +=
this.listRef.scrollHeight - this.prevScrollHeight;
this.prevScrollHeight = null;
}
}
render() {
return (
<div ref={this.setListRef}>
{/* ...contents... */}
</div>
);
}
setListRef = ref => {
this.listRef = ref;
};
}
Loading…
Cancel
Save