简体   繁体   中英

Rendering component before fetching data has been finished

I'm trying to fetch data by creating a function. In that function I am doing trying to set state and I am calling it from the componentDidMount method, but I am having a few problems:

  1. I am not sure if while is good practice to be used, because I am looping and changing my endpoint so I can get new data every time.
  2. I have tried to return data from the fetching function and use setState inside componentDidMount , but I had a problem, I suspect because componentDidMount is running before fetching has completed
  3. I have tried to use res.json() on the data using a promise, but I got an error that res.json is not a function.
state = {
    title: [],
    image: [],
    rating: [],
  };

getData =  () => {
    let i = 1;
    while (i <= 9) {
       axios.get(`http://api.tvmaze.com/shows/${i}`)
      .then(response => console.log(response))
      .then(response => this.setState({
        title:response.data.data.name[i],
      }))
      .catch(error => console.log(error));
      i++;
    }
  };
  componentDidMount() {
    this.getData();
    console.log(this.state.title);
  }

The way in which you are setting state will result in the last data from api to be saved in state and it will render only last call

Do it like this

getData =  () => {
let i = 1;
while (i <= 9) {
   axios.get(`http://api.tvmaze.com/shows/${i}`)
  .then(response =>{
let prevState=this.state.title
prevState.push(response.data.data.name[i])
   this.setState({
    title:prevState,
  })})
  .catch(error => console.log(error));
  i++;
}

};

If your goal is to render your JSX after you're done fetching information, then I'd suggest creating an additional item in your state , isLoading , that you can set to true or false and render your JSX conditionally.

Based on the example you provided below, it'd look like the following:

class Shows extends React.Component {
  state = { 
    title: [], 
    image: [],
    rating: [],
    isLoading: true
  }

  componentDidMount() {
    this.getData()
  }

  getData = () => {
    // I've created a URL for each request
    const requestUrls = Array.from({ length: 9 })
      .map((_, idx) => `http://api.tvmaze.com/shows/${idx + 1}`);

    const handleResponse = (data) => {
      // `data` is an array of all shows that you've requested

      // extract information about each show from the payload
      const shows = data.map(show => show.data)

      // handle shows data however you need it
      // and don't forget to set `isLoading` state to `false`
      this.setState({
        isLoading: false,
        title: shows.map(show => show.name),
        image: shows.map(show => show.url),
        rating: shows.map(show => show.rating.average),
      })
    }
    const handleError = (error) => {
      // handle errors appropriately
      // and don't forget to set `isLoading` to `false`
      this.setState({
        isLoading: false
      })
    }

    // use `Promise.all()` to trigger all API requests
    // and resolve when all requests are completed
    Promise.all(
      requestUrls.map(url => axios.get(url))
    )
      .then(handleResponse)
      .catch(handleError)
  }

  render() {
    const { isLoading, title, image, rating } = this.state

    // prevent showing your `JSX` unless data has been fetched
    // ideally, show a loading spinner or something that will
    // tell users that things are happening; 
    // returning `null` won't render anything at all
    if (isLoading) {
      return null
    }
    return (
      <div>...</div>
    )
  }
}

This way, with Promise.all , it's a lot easier to reason about all these calls that you're making.

Other than that, using componentDidMount to fetch data from an API is the right place to do it, but I'd stay away from the while loop and use Promise.all for all your requests and map to create an array of promises (requests) that can be passed to Promise.all and handled all at once.

Working example:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM