1

This is a simplified version of a problem I've faced while working in React. When I'm making a fetch call inside componentDidMount() and updating the state with the payload as follows:

componentDidMount(){
    fetch("/api/endpoint").then(data => {
        return data.json();
        }).then(json => {
            this.setState({
                data: json
            });
    });
}

And rendering it out in render():

render(){
    return(
        <p>{this.state.data.title}</p>
    )
}

I get an error saying this.state.data is undefined. I got around it by wrapping it around a conditional operator as follows:

{ this.state.data !== undefined ? <p>{this.state.data.title}</p> : null }

But my question is, if componentDidMount() fires before render() then how can this.state.data ever be undefined?

blankface
  • 5,757
  • 19
  • 67
  • 114

3 Answers3

0

componentDidMount() runs after render(). You need to initialize the state correctly in the constructor

class YourComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: {title: "" } };
  }

  componentDidMount(){
      fetch("/api/endpoint").then(data => {
          return data.json();
          }).then(json => {
              this.setState({
                  data: json
              });
      });
  }
  render(){
    return(
        <p>{this.state.data.title}</p>
    )
  }

}
SrThompson
  • 5,568
  • 2
  • 17
  • 25
  • I don't think that's the issue here. Even if you treat `this.state.data` as a string, it will still return undefined. – blankface Feb 01 '18 at 03:31
  • Initializing the state in this manner won't return undefined, unless the fetch call is returning undefined – SrThompson Feb 01 '18 at 03:46
0

But my question is, if componentDidMount() fires before render() then how can this.state.data ever be undefined?

The order of componentDidMount firing before or after render doesn't really matter here. componentDidMount has async effects. Let's say it always fires before render, then what happens is this:

componentDidMount is run. It runs fetch. Running fetch doesn't make the browser wait for fetch to return and do setState. Instead fetch sends the request to the server, and returns a promise that will be fulfilled when the response from the server returns.

The code that runs when the promise is fulfilled is the function you pass as an argument to .then(). Since your setState call is inside .then(), it will only run once the response it available.

Meanwhile, your browser goes ahead and calls render. The response from the server may or may not have returned and resolved the promise (most probably it won't have returned because network is slower than the processor executing code). So render gets called with this.state.data not yet defined. So you need to consider a state where data is undefined in render.

Hammad Akhwand
  • 779
  • 1
  • 7
  • 19
0

But my question is, if componentDidMount() fires before render() then how can this.state.data ever be undefined?

You assumption/understanding that componentDidMount lifecycle function fires before render is wrong, componentWillMount is fired before render while componentDidMount is fired after the render method. Now you might say that since componentWillMount is fired before render, I can move my fetch call in componentWillMount and thus I would not have to add a check in the render method, but that not right. Even though your request is fired before the render the response may not always be available before the render function is executed and since all of these happen asynchronously you need to add a check for the data in render method

render(){
    return(
        <p>{this.state.data? this.state.data.title: null}</p>
    )
  }

Check Use componentWillMount or componentDidMount lifecycle functions for async request in React question for more details

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400