Practice
The new getSnapshotBeforeUpdate() lifecycle method is called right before the DOM is updated. The value returned from this method will be passed as the third parameter to
class MyComponent extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
// ...
}
}
This lifecycle method, together with componentDidUpdate()
covers all
the use cases of componentWillUpdate().
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the scroll position so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
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) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
In the example above, componentWillUpdate is used to read a DOM property. However, with async rendering there may be delays between the «render» phase lifecycles (such as componentWillUpdate and render) and the «commit» phase lifecycles (such as componentDidUpdate). If the user does something like resizing the window during that time, the scrollHeight value read in componentWillUpdate will be stale.
The solution to this problem is to use the new «commit» phase lifecycle, getSnapshotBeforeUpdate. This method is called immediately before mutations are made (for example, before the DOM is updated). It can return a value that React passes as a parameter to componentDidUpdate, which is called immediately after the mutations.
In other words: React 16.6 introduced a new feature called «Suspense». This feature enables asynchronous rendering — rendering a subtree of react components can be deferred (for example, to wait for a network resource to load). It is also used internally by React to prioritise important DOM updates over others in order to improve perceived rendering performance. This can, as you would expect, cause substantial delays between rendering the virtual DOM on the react side (which triggers componentWillUpdateand render(), but may contain some asynchronous component subtree that has to be awaited) and the actual reflection in the DOM (which triggerscomponentDidUpdate). In older versions of React before Suspense, these lifecycle hooks were always called with very little delay, because rendering was fully synchronous, which justified the pattern of gathering DOM information in componentWillUpdateand using it in componentDidUpdate, which is no longer the case.
Comments