42

In the react docs it recommends making initial network requests in the componentDidMount method:

componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.

If componentWillMount is called before rendering the component, isn't it better to make the request and set the state here? If I do so in componentDidMount, the component is rendered, the request is made, the state is changed, then the component is re-rendered. Why isn't it better to make the request before anything is rendered?

TimothyBuktu
  • 2,016
  • 5
  • 21
  • 35
  • 2
    Request MAY not finished yet when component is rendered even when you call it from `componentWillMount` method. – Niyoko Jan 12 '17 at 11:41
  • For anyone coming here in 2022+, componentWillMount is deprecated so the answer is now clear. – Ben Jones Jan 14 '22 at 17:22

4 Answers4

105

You should do requests in componentDidMount.

If componentWillMount is called before rendering the component, isn't it better to make the request and set the state here?

No because the request won’t finish by the time the component is rendered anyway.

If I do so in componentDidMount, the component is rendered, the request is made, the state is changed, then the component is re-rendered. Why isn't it better to make the request before anything is rendered?

Because any network request is asynchronous. You can't avoid a second render anyway unless you cached the data (and in this case you wouldn't need to fire the request at all). You can’t avoid a second render by firing it earlier. It won’t help.

In future versions of React we expect that componentWillMount will fire more than once in some cases, so you should use componentDidMount for network requests.

Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
  • 6
    Usually I will use a `isFetching` state for network requesting. 1. Initially `isFetching = false`, render with empty data. 2. `componentDidMount`, dispatch fetch request, set `isFetching` to `true`, render with a `` component. 3. Fetch successfully, set `isFetching` to `false`, render with fetched data. Is there a good(safe) way to initially rendered with a `` component, rather than rendering with empty data. Thanks . – Vincent Bel Jan 14 '17 at 14:54
  • 2
    Another option might be to fetch network data from the parent component and then pass it to the child component via props, thus avoid second render. – David Jan 16 '17 at 13:07
  • I know this is now out of scope since componentWillMount is deprecated but what if the network call is synchronous? – Aanchal1103 Feb 21 '19 at 07:33
  • Sorry if my question is stupid. If I fetch data in `componentWillMount`, even if it does not break the component that is rendered for the first time, whether the fetch data function in` componentWillMount` will be called a bit earlier than the fetch data `componentDidMount` function, resulting in Do we get the data a little faster? – Duy Hưng Androgyne Tenor May 06 '20 at 11:06
6

You should use componentDidMount.

Why isn't it better to make the request before anything is rendered?

Because:

  • Your request will almost certainly not finish before the component is rendered (unless rendering large amounts of markup, or you are on a zero latency quantum entanglement connection), and the component will ultimately need to re-render again, most of the time
  • componentWillMount is also called during server-side rendering (if applicable)

However, if you were to ask, isn't it better to initiate a request in componentWillMount (without actually handling it in place), I would definitely say yes (ES6), and I do this myself to occasionally cut a few milliseconds from load times:

componentWillMount() {
    // if window && window.XMLHttpRequest
    if (!this.requestPromise) {
        this.requestPromise = new Promise(resolve => {
            // ... perform request here, then call resolve() when done.
        });
    }
}

componentDidMount() {
    this.requestPromise.then(data => ...);
}

This will start preloading your request during componentWillMount, but the request is only handled in componentDidMount, whether it is already finished by then or still in progress.

John Weisz
  • 30,137
  • 13
  • 89
  • 132
4

You should make the request in componentDidMount as no side-effects requests should be made in componentWillMount. It's fine to setState in componentWillMount, if you setState in componentDidMount you will immediately trigger a second re-render.

You will read that it's an anti-pattern (UGHHH) and some linters have it prohibited (eslint-react-plugin), but I wouldn't pay huge attention to that as sometimes it's the only way to interact with the DOM. You can set your default state either in willMount or as a method property ( state = { } ), if you're using the associated babel stage

As you say the component will be rendered already once, but this is good because you can display some kind of Loader or any other form of information that a resource is loading.

class MyComp extends Component {

    // What I do with stage 0
    state = { mystate: 1 }

    // What you might want to do if you're not 
    // on the experimental stage, no need to do 
    // the whole constructor boilerplate
    componentWillMount() {
        this.setState({ mystate: 1 });
    }

    componentDidMount() {
        dispatch(yourAction());

        // It's fine to setState here if you need to access 
        // the rendered DOM, or alternatively you can use the ref
        // functions
    }

    render() {
        if (!this.props.myCollection) return <Loader />

        return (
           <div> // your data are loaded </div>
        )
    }
}
Foxhoundn
  • 1,766
  • 2
  • 13
  • 19
3

The real reason to avoid fetches in lifecycle hooks before the render method is because the React community is planning to make the render method calls asynchronous.

Check the response from gaeron here : https://github.com/reactjs/reactjs.org/issues/302

Now this means placing an asynchronous action like fetch(or any asynchronous operation for that matter) in any of the lifecycle methods before render, will interfere with the rendering process.

So unlike the synchronous execution that you would imagine today :

1. constructor << imagine firing fetch() here >> => 2. getDerivedStateFromProps << fetch completes, callback added to callback queue by event loop >> => 3. render => 4. componentDidMount => 5. callbacks executed in the order that they were added so fetch callback runs now.

it would instead be like this :

1. constructor << imagine firing fetch() here >> => 2. getDerivedStateFromProps << in the meantime, fetch completes and callback gets queued >> << imagine firing async render() here. its callback too gets queued >> => callbacks get executed in the order that they were added 3. so fetch callback runs first => 4. render callback runs => 5. componentDidMount

This interference may result in state changes getting reverted because render may apply an earlier state that overrides the changes made by fetch.

Another other reason being that fact that it is the componentDidMount lifecycle that guarantees the presence of the corresponding component on DOM and if fetch tries to manipulate the DOM even before its available or is updated, it could result in faulty application display.

supi
  • 2,172
  • 18
  • 15