0

I'm trying to fetch some JSON data from a URL. I'm then setting my state to be a portion of that data.

In my render method, I'd like to the display that data using map()

The issue I'm facing is that because it takes some time to fetch the data, the state is still set to null by the time it tries to render it.

Another issue I'm having is that my getData() function seems to fire repeatedly every few seconds. If I add a console log at the end it logs it over and over again.

Would anyone be able to let me know if they can see me doing something wrong?

My code is as follows:

getData = () => {
    let _this = this
    fetch('https://my-data-link.com')
    .then(
      response => {
        if (response.status !== 200) {
          console.log('Looks like there was a problem. Status Code: ' +
            response.status);
          return;
        }

        // Examine the text in the response
        response.json().then(
          data => {
            _this.setState({data: data.searchResults.filter(d => d.salesInfo.pricing.monthlyPayment <= _this.state.monthlyMax)})
          }
        );
      }
    )
    .catch(function(err) {
      console.log('Fetch Error :-S', err);
    });
    
    // Adding a console log here is when I noticed that the code keeps firing.
  }

  renderData = () => {
    let vehicles = "Loading..."
    let _this = this
    this.getData()
    setTimeout(function(){
      vehicles = _this.state.data.map(vehicle => (
        <h1>{vehicle.make}</h1>
      ))
      return vehicles
    }, 6000);

    return vehicles
  }
  
  render() {
    return (
      {this.state.formSubmitted > 0 &&
        <div>
          <h3 className="ch-mt--4">Recommended vehicles</h3>
          {this.renderData()}
        </div>
      }
    )
  }
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Eli Nathan
  • 1,020
  • 2
  • 13
  • 35

2 Answers2

1

You have two issues

First: You are calling getData in render method and in getData you are calling setState which essentially triggers a loop. Trigger it in componentDidMount if the it needs to fire just once on component mount.

Second: Instead of setTimeout which isn't reliable, you should initialise the state data to be empty array in constructor

constructor(props) {
    super(props);
    this.state = {
        data: [];
    }
}
componentDidMount() {
     this.getData();
}

getData = () => {
    let _this = this
    fetch('https://my-data-link.com')
    .then(
      response => {
        if (response.status !== 200) {
          console.log('Looks like there was a problem. Status Code: ' +
            response.status);
          return;
        }

        // Examine the text in the response
        response.json().then(
          data => {
            _this.setState({data: data.searchResults.filter(d => d.salesInfo.pricing.monthlyPayment <= _this.state.monthlyMax)})
          }
        );
      }
    )
    .catch(function(err) {
      console.log('Fetch Error :-S', err);
    });


  }

  renderData = () => {
    let vehicles = "Loading..."
    if(this.state.data.length === 0) {
        return vehicles;
    }
    return this.state.data.map(vehicle => (
        <h1>{vehicle.make}</h1>
    ));
  }

  render() {
    return (
      {this.state.formSubmitted > 0 &&
        <div>
          <h3 className="ch-mt--4">Recommended vehicles</h3>
          {this.renderData()}
        </div>
      }
    )
  }
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
1

You should call getData in componentDidMount as follows:

componentDidMount() {
  this.getData()
}

Moreover you cannot call setState in render method it will be infinite loop as after setting state render method is called, as renderData is setting state which is called in render method

Instead of setTimeOut you should do following in render method

// in return of render method. It will display data from state after api call
{this.state.data && this.state.data.map(vehicle => (
    <h1>{vehicle.make}</h1>
))}
Umair Farooq
  • 1,763
  • 13
  • 16