0

I am trying to fetch data from an api and push it into an array and then map through it and render it on page but when i try to map through array i got nothing. Here is my code:

    let dataUrlNoPage = `https://api.themoviedb.org/3/movie/top_rated?api_key=${process.env.REACT_APP_TMDB_KEY}&language=en-US&page=`;
    let top100Array = [];

    const fetchData = () => {
for (let i = 1; i <= 5; i++) {
  fetch(dataUrlNoPage + i)
    .then((res) => res.json())
    .then((data) => {
      console.log(data);
      if (!data.errors) {
        for (let i = 0; i < data.results.length; i++) {
          top100Array.push(data.results[i]);
        }
        return top100Array;
      } else {
        setResults([]);
        console.log('error');
      }
    });
}
};

 fetchData();

console.log(top100Array);

I can see top100Array in console right here.

const listItems = top100Array.map((movie) => (
<li key={movie.id}>
  <TopListCard movie={movie} />
</li>
));

 return (
<div className="container">
  <div className="row">
    <div className="col s12 m6">
      <div className="add-content">
        <h1>Top 20</h1>

        <ul className="results">{listItems}</ul>
        {console.log(listItems)}
      </div>
    </div>
  </div>
</div>
);
};

But i get empty page here. Thanks for the help.

vgnrd
  • 7
  • 3
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – evolutionxbox Nov 02 '20 at 18:41

3 Answers3

3

Rather than mutating the top100Array (and then returning it from the .then for some reason), you should use state that gets set with the asynchronous results, so as to force a re-render. Use Promise.all to wait for each fetch to finish.

const [results, setResults] = useState();
const getData = i => fetch(dataUrlNoPage + i)
  .then((res) => res.json())
  .then((data) => {
    if (data.errors) {
      console.log(data.errors);
      throw new Error(data.errors);
    }
    return data.results;
// Retrieve results only once, on mount:
useEffect(() => {
  Promise.all(
    Array.from({ length: 5 }, (_, i) => getData(i + 1))
  )
    .then((allResults) => {
      setResults(allResults.flat());
    })
    .catch(handleErrors);
}, []);

Then render it:

<ul className="results">{results ? listItems(results) : null}</ul>

replacing the JSON.stringify with however you want to transform the resulting array into JSX elements. (maybe you'd want results.map(result => <span>result.title</span>), or something like that)

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • This causes infinite loop any ideas why? – vgnrd Nov 02 '20 at 18:58
  • Oops, I forgot the `useEffect` - need to make sure the API is fetched only on mount, rather than on every render. See edit. – CertainPerformance Nov 02 '20 at 18:59
  • The infinite loop is solved but still not working. It renders [null,null,null,null,null] and when i log results it shows undefined array of 5. – vgnrd Nov 02 '20 at 19:04
  • Are you handling errors after the `Promise.all` ends, as in the answer's code? If you're handling the errors individually and returning `null` inside each iteration instead, your `nulls` could be the result. The array is initially undefined - it gets populated asynchronously, after which there's a re-render. – CertainPerformance Nov 02 '20 at 19:12
  • What you meant by handling errors i dont quite get it. What should i do in handleErrors? – vgnrd Nov 02 '20 at 19:21
  • Whatever you need to do to handle errors - it's up to you. Do you want to `console.log`, or set an error state which gets displayed to the user, or something else? Just have *something* in the `.catch`, so as to avoid unhandled rejections. – CertainPerformance Nov 02 '20 at 19:22
-1

Not sure what is returned and the for loop is not optimal, but you might as well map directly in the jsx

<ul className='results'>
  {top100Array.map((movie) => (
    <li key={movie.id}>
      <TopListCard movie={movie} />
    </li>
  ))}
</ul>
Julian Kleine
  • 1,539
  • 6
  • 14
-1

Everything looks fine to me. You actually don't need to return top100Array. The only thing I can think of is that you render your list before top100Array is filled with items.

SaizFerri
  • 21
  • 4