Home > Blockchain >  React: useState(): Fetching data during an ongoing fetch does not work
React: useState(): Fetching data during an ongoing fetch does not work

Time:04-16

I want to display a list of the New York Times bestsellers by fetching the data via an API. But, the covers' links have to be fetched from the Google Books API, since the NYT API does not provide them.

Therefore, I want to map a list with the fetched data. In the same step in which I fetch the data from the NYT API, the data from the Google Books API shall be fetched, too. So, I use the await keyword to let the code wait for the second fetch to be finished.

But, unfortunately, the list does not update though the promise has been fulfilled, and the data has been fetched. The bestsellerlist variable is an array of fulfilled promises, but no "real array" I could access.

I guess it should be possible to split the fetch, and create the list out of two arrays: One with the NYT API data, and one with the Google Books API data. Nevertheless, I wonder how I can fix the code, so I can fetch all the data and create the list in one step.

const fetchLinkCover = async (isbn) => {
    const res = await fetch(googleApi(isbn));
    const json = await res.json();
    return json.items[0].volumeInfo.imageLinks.smallThumbnail;
  };
useEffect(() => {
    fetch(nytimesApi)
      .then((res) => res.json())
      .then((json) => {
        const fetchedBookList = json.results.books;
        setBestsellerList(
          fetchedBookList.map(async (item) => {
            const linkCover = await fetchLinkCover(item.isbns[0].isbn13);
            return {
              title: item.title,
              authors: item.author,
              ranking: item.rank,
              cover: linkCover,
            };
          })
        );
      });
  }, []);
console.log(bestsellerList);

console after executing the code

CodePudding user response:

.map doesn't do anything special with promises. If you return a promise from your map function (and async functions always return promises), then .map will create an array of promises. It won't wait for those promises to resolve.

To wait for an array of promises, you can use Promise.all to combine them together into a single promise. Then you either call .then on that promise, or you put your code in an async function and await it.

With .then:

fetch(nytimesApi)
  .then((res) => res.json())
  .then((json) => {
    const fetchedBookList = json.results.books;
    const promises = fetchedBookList.map(async (item) => {
      const linkCover = await fetchLinkCover(item.isbns[0].isbn13);
      return {
        title: item.title,
        authors: item.author,
        ranking: item.rank,
        cover: linkCover,
      };
    });
    return Promise.all(promises);
  })
  .then((books) => {
    setBestsellerList(books);
  });

Or with async/await:

useEffect(() => {
  const fetchBooks = async () => {
    const res = await fetch(nytimesApi);
    const json = await res.json();
    const fetchedBookList = json.results.books;
    const promises = fetchedBookList.map(async (item) => {
      const linkCover = await fetchLinkCover(item.isbns[0].isbn13);
      return {
        title: item.title,
        authors: item.author,
        ranking: item.rank,
        cover: linkCover,
      };
    });
    const books = await Promise.all(promises);
    setBestsellerList(books);
  };
  fetchBooks();
}, []);

  • Related