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:
-
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. -
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 incomponentDidMount
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.