Linguine Code

How does React componentDidUpdate work

I previously wrote an article about componentDidMount, and how that’s a great lifecycle to make API calls, and to make DOM manipulation.

But I had a couple questions about another use case. These are questions that you may have as well.

  • Is there a better place to make DOM manipulation?
  • How to do I continue making API calls after React state or props have changed?

After doing a bit of Googling, I found out about componentDidUpdate.

This was the answer to my problems, and it might be yours too.

What is componentDidUpdate?

This is a lifecycle in React that gets called right after a change in props or state change has occurred.


componentDidUpdate(prevProps, prevState, snapshot)

This method provides the previous props and state values. This way it allows you to do a comparison of a before and current snapshot.


componentDidUpdate(prevProps, prevState) {
  if (prevProps.foo !== this.props.foo) {
    // ... do something
  }
}

If you’re curious to know what the snapshot variable is about checkout this article.

When does componentDidUpdate get called?

To be more specific here is how the the breakdown looks like


componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()

componentDidUpdate is always the last lifecycle to get called for a React component.

componentDidUpdate does not get called after the first initial render() lifecycle.

When to use componentDidUpdate method?

Well this goes back to my initial problems.

DOM manipulation after render

Sometimes third party libraries don’t have a React version, and requires to manipulate the DOM directly.

For example, this site uses PrismJS to highlight code blocks.

And the content is being brought over an API. I need to highlight all the code blocks that are found once the content is dumped onto the page.


import Prism from 'prismjs';

class PostPage extends React.Component {

  componentDidMount() {
    // Takes care of the first render
    Prism.highlightAll();
  }

  componentDidUpdate() {
    // Takes care of all other renders
    Prism.highlightAll();
  }

  render() {
    // ... rendering API content
  }
}

In the example above, I’m using the componentDidMount lifecycle to highlight all <code> blocks.

And just incase the PostPage component is going to get new props or state updates, I need run Prism.highlightAll() again to make sure my <code> blocks stay pretty.

API Calls

This is also a great place to make additional API calls after a certain React prop or state has changed.

For example, let’s say a React component can only grab user data after certain prop has been passed down.


class UserNavBar extends React.Component {
  
  componentDidUpdate() {
    if (this.props.isAuth) {
      // ... fetch user data (profile pic, name, email, etc)
    }
  }

  render() {
    // ... rendering user nav bar content
  }
}

class AuthWrapper extends React.Component {
  state = {
    isAuth: false,
  };

  componentDidMount() {
    this.setState({ isAuth: true })
  }

  render() {
    return <UserNavBar isAuth={this.state.isAuth} />
  }
}

Change state or dispatch with Redux

This is also a great place to update your React state or stores (Redux or Mobx) after a certain value has been presented.

I’m going to add a bit more to the UserNavBar component I created above.

I’m going to cache the data on to it’s local React state, so I don’t have to constantly make fetch calls.


class UserNavBar extends React.Component {

  state = {
    user: null,
  };

  componentDidUpdate() {
    if (this.props.isAuth) {
      fetch('https://jsonplaceholder.typicode.com/users/1')
        .then(res => res.json())
        .then(data => this.setState({ user: data }));
    }
  }

  render() {
    // ... rendering user nav bar content
  }
}

Beautiful!

Whenever the AuthWrapper component says my user has been authenticated, my UserNavBar component will fetch the data.

But, there is one big problem here! componentDidUpdate gets called after every prop or state has changed.

And from the look above, my userNavBar will be an infinite loop of state change.

Avoid infinite loop in componentDidUpdate

So you need to be very careful, and add good conditionals if you modify state or stores within componentDidUpdate.

I’m going to update my UserNavBar componentDidUpdate method.


componentDidUpdate() {
  if (this.props.isAuth && !this.state.user) {
    fetch('https://jsonplaceholder.typicode.com/users/1')
      .then(res => res.json())
      .then(data => this.setState({ user: data }));
  }
}

Now I’m checking to make sure the state property user is falsey before I fetch.

Conclusion

Just to recap what was covered in this article. The componentDidUpdate get’s called after the React component props or state has changed.

This method is best use for:

  • DOM manipulation after the component has rendered
  • API calls after specific conditions have been met
  • Update React state or stores like Redux, and Mobx after a set of conditions have been met

Use good conditions to avoid infinite loops of updates, and unnecessary API calls.