vgnrd
vgnrd

Reputation: 7

Cant map through array an Array of Objects

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.

Upvotes: 0

Views: 120

Answers (3)

CertainPerformance
CertainPerformance

Reputation: 371168

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)

Upvotes: 3

SaizFerri
SaizFerri

Reputation: 21

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.

Upvotes: -1

Julian Kleine
Julian Kleine

Reputation: 1547

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>

Upvotes: -1

Related Questions