Where to Fetch Data: componentWillMount vs componentDidMount

By Dave Ceddia  — updated Comment

When you need to fetch some data for a React component, where do you do it?

This question comes up all the time.

There are two common places to fetch data in class components, and both are lifecycle methods:

  • componentWillMount
  • componentDidMount

With the addition of React Hooks, there’s a new place to fetch data with the useEffectHook. Read that article for more on useEffect; in this article I’ll focus on class components.

And just to be clear, the render function is never a good place to fetch data – or to do anything that’s asynchronous, that changes state in some way, or that causes side effects. The only thing render should do is return some JSX to display, and maybe spend a few lines preparing that data to display.

Let’s look at the two common options and the pros and cons of the componentWillMount and componentDidMount lifecycle methods.

componentWillMount

This looks like the logical place to fetch data. Fetch it just before the component will mount, right?

There are a couple problems though.

First, the big one: componentWillMount is deprecated as of React 16.3 (March 2018). Until React 17, that name will continue to work – but this is a warning to move away from it as soon as you can. In place of it, you can use the constructor in a class component. But if you’re currently fetching data in componentWillMount, keep reading…

Here’s the second “gotcha”, and it’s a bit unintuitive: An API call with fetch or axios inside componentWillMount will not return before the first render. This means the component will render with empty data at least once.

Because of the nature of async events in JavaScript, when you kick off an API call, the browser goes back to doing other work in the meantime. When React is rendering a component, it doesn’t wait for componentWillMount to finish whatever it started – React marches on and continues to render.

There is no way to “pause” rendering to wait for data to arrive. You cannot return a promise from componentWillMount or wrangle in a setTimeout somehow. The right way to handle this is to setup the component’s initial state so that even when it renders with no data, it still looks acceptable.

So what can you do? You could render an empty list, or maybe show a little hint to the new user about how to get started. Whatever you do, don’t try to iterate over an array of undefined or you’ll get the dreaded “Cannot read property ‘map’ of undefined” error.

componentDidMount

By the time componentDidMount is called, the component has been rendered once.

In practice, componentDidMount is the best place to put calls to fetch data, for two reasons:

  1. Using didMount makes it clear that data won’t be loaded until after the initial render. This reminds you to set up initial state properly, so you don’t end up with undefined state that causes errors.

  2. If you ever need to render your app on the server (a.k.a. server-side-rendering/SSR with Next.js or similar), componentWillMount will actually be called twice – once on the server, and again on the client – which is probably not what you want. Putting your API call code in componentDidMount will ensure that data is only fetched from the client, where it should be.

Wrap Up

I hope this clears up the question of where to load data. If you’re still not sure of the best way how to actually make the AJAX call and load data, read more about API calls in React.

Learning React can be a struggle -- so many libraries and tools!
My advice? Ignore all of them :)
For a step-by-step approach, read my book Pure React.

Loved it! Very well written and put together. Love that you focused only on React. Wish I had stumbled onto your book first before I messed around with all those scary "boilerplate" projects.
— John Lyon-Smith
comments powered by Disqus