What is getSnapshotBeforeUpdate() in React?

I made a very simple project for you to understand when we should use getSnapshotBeforeUpdate life cycle method and I used getSnapshotBeforeUpdate to store user scroll position and used that in componentDidUpdate

github repository https://github.com/mahdimehrabi/getSnapShot-sample

demo https://mahdimehrabi.github.io/js/snapshot/


Well, actually getSnapshotBeforeUpdate() runs after the update.

Consider this which kinda explains everything:

class Demo extends React.Component {
    constructor(props) {
      super(props);
      this.state = {x:1};
      console.log(1);
   }
   componentDidMount() {
      this.setState({x:2});
      console.log(3);
   }
   shouldComponentUpdate() {
      console.log(4);
      return true;
   }
   getSnapshotBeforeUpdate(prevProps, prevState){
      console.log(5,prevState.x,this.state.x);
      return 999;
   }
   componentDidUpdate(prevProps, prevState, snapshot) {
      console.log(6,snapshot);
   }
   componentWillUnmount() {
      console.log(7);
   }
   render() {
      console.log(2);
      return null;
   }
}

ReactDOM.render(
   <Demo />,
   document.querySelector('div')
);
ReactDOM.unmountComponentAtNode(document.querySelector('div'));

The output is:

1
2
3
4
2
5 1 2
6 999
7

The main difference is getSnapshotBeforeUpdate runs before the update, componentDidUpdate runs after.

So if there is anything you need to save before it gets overwritten, that's what getSnapshotBeforeUpdate is for. These are usually externally managed things (uncontrolled in React terms), such as the scrollPosition in your example, or when interoping with other libraries outside React (e.g. a jQuery plugin).

The main guideline is that if you are unsure, you probably don't need it. If you do, you will know it.


The two paragraphs above the example you quoted explain the need for that:

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). If the user does something like resize the window during this time, the scrollHeight 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.

In other words: React 16.6 introduced a new feature called "Suspense". This feature enables async rendering - the rendering of a subtree of react components can be delayed (for example to wait for a network resource to load). It is also used internally by React to favor important DOM updates over others to increase the perceived rendering performance. This can - as one would expect - cause substantial delays between the react-side virtual DOM rendering (which triggers componentWillUpdate and render(), but may contain some async component subtree which has to be awaited) and the actual reflection to the DOM (which triggers componentDidUpdate). In older react versions before Suspense these lifecycle hooks were always called with very little delay because the rendering was completely synchronous, which justified the pattern to gather DOM information in componentWillUpdate and use it in componentDidUpdate, which is no longer the case.