Linguine Code

What is React getSnapshotBeforeUpdate

Since the release of React Suspense, rendering components are becoming more asynchronous.

This is a good thing for folks with limited bandwidth or weak devices.

In a previous article I wrote about doing DOM manipulation within the React componentDidUpdate lifecycle.

This was a good solution to do DOM manipulation because it was the final commit lifecycle of a React component.

But that won’t be the case any longer since lazy loading React components may cause a delay in rendering elements.

The new lifecycle method in React


getSnapshotBeforeUpdate(prevProps, prevState) {}

This method may look familiar to the componentDidUpdate lifecycle. It’s given the previous props and the previous state, but it has 1 major difference.

getSnapshotBeforeUpdate gets called before the most recent render() output.

It allows the engineer to capture some information about the DOM before it’s changed.

This method should return a value or null.

How getSnapshotBeforeUpdate works with componentDidUpdate


componentDidUpdate(prevProps, prevState, snapshot) {
  console.log(snapshot) // undefined
}

If you see the code example above, snapshot will be undefined.

To make it have a value, you must use the getSnapshotBeforeUpdate method and return a value.


getSnapshotBeforeUpdate() {
  return {foo: 1};
}

componentDidUpdate(prevProps, prevState, snapshot) {
  console.log(snapshot) // { foo: 1 }
}

As you can see in the code above, I’m using getSnapshotBeforeUpdate and returning an object.


{ foo: 1 }

I then print out the snapshot variable within the componentDidUpdate method and you will see the object that was return in getSnapshotBeforeUpdate.

When to use getSnapshotBeforeUpdate

As mentioned before this is a great method to capture some information about the DOM before it’s changed.

Let’s look at a simple example.

The goal is to override the box color whenever the user goes from biggest box size to any of the other smaller sizes.


class App extends React.Component {
  state = {
    boxSize: "normal"
  };

  // Defines box sizes with background color
  boxes = {
    small: {
      height: 60,
      width: 60,
      backgroundColor: "yellow"
    },
    normal: {
      height: 120,
      width: 120,
      backgroundColor: "red"
    },
    big: {
      height: 180,
      width: 180,
      backgroundColor: "blue"
    }
  };

  // Get box DOM reference
  boxRef = React.createRef();

  handleClick = value => () => this.setState({ boxSize: value });

  getSnapshotBeforeUpdate() {
    // Let componentDidUpdate know whether to override the box
    // color or not.
    return {
      overrideBoxColor: this.boxRef.current.offsetHeight > 120
    };
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // Override the box ref directly
    if (snapshot.overrideBoxColor) {
      this.boxRef.current.style.backgroundColor = '#000';
    }
  }

  render() {
    return (
      <>
        <div>
          <button onClick={this.handleClick("small")}>Shrink</button>
          <button onClick={this.handleClick("normal")}>Normal</button>
          <button onClick={this.handleClick("big")}>Size up</button>
        </div>
        <div ref={this.boxRef} style={this.boxes[this.state.boxSize]} />
      </>
    );
  }
}

If you’d like a better example, check out the React docs.

Happy coding!