0

I am trying to store data from the TMDB movie api using a custom react hook .

useFetch.js

import {React,useState,useEffect} from 'react';


export default function useFetch() {

  const key = process.env.REACT_APP_API_KEY;
  
  //the urls I want to get data from     
  const specificTypes = [
    {'Type':'Top Rated', 'url' :`https://api.themoviedb.org/3/movie/top_rated?api_key=${key}`},
    {'Type':'Trending', 'url' :`https://api.themoviedb.org/3/trending/movie/day?api_key=${key}`},
  ];

 

  const [movieTypes,setMovieTypes] = useState([]); //this is where I want to store url data 

  useEffect(()=>{
    
    const fetchSpecificTypes = async ()=>{
      
       const promises = [];
       specificTypes.map(async type=>{
         let response = await fetch(type.url);
         let res = await response.json();
         promises.push(res.results);
       });
       console.log({promises}); data is stored in promises successfully
       setMovieTypes(promises); //how I try to set the movies 


    } 

    fetchSpecificTypes();

  },[]);

  return {movieTypes};

}

When I console.log({promises}) I get this object where the 2 items are the movie types with 20 movies inside : enter image description here And then when I try to display the movies from the object above in another component :

MovieList.js

    import {React , useState,useEffect} from 'react'
    import useFetch from './useFetch';
    import '../App.css';
    
    export default function MovieList() {
  
  const {movieTypes} = useFetch();
  const baseImgUrl = "https://image.tmdb.org/t/p";
  const size = "/w400";
  

  return (
    <div className="movie-list">
      {
        movieTypes.map(movie=>{
          return (
            <>
              {
                Object.values(movie).map((val,k)=>{
                    let path = baseImgUrl + size + val.backdrop_path; //full image path
                    return <div className= "movie-item" key = {val.id}>
                              <img alt = "movie" src = {path}/>
                              <h4 className="movie-overview"> {val.overview} </h4>
                              <p className="movie-title">{val.title}</p>
                    </div>
                })
              }
            </>
          )
        })  
      }
    </div>
  );
}

I get nothing no movies displayed . I would appreciate your help with this .

3 Answers 3

2

Await is not working as expected inside Array.map(). You should either use for loop or use Promise.all()

const fetchSpecificTypes = async() => {
  const promises = [];
  for(let i=0; i<specificTypes.length; i++) {
    let response = await fetch(specificTypes[i].url);
    let res = await response.json();
    promises.push(res.results);
  }
  setMovies(promises);
}

const fetchSpecificTypes = async() => {
  const results = await Promise.all(specificTypes.map(type => fetch(type.url)));
  const results2 = await Promise.all(results.map(res => res.json());
  const movies = results2.map(res => res.results);
  setMovies(movies);
}
Sign up to request clarification or add additional context in comments.

3 Comments

i just added the code that uses Promise.all.
Wong With your example I fetched the movies correctly . Now I am trying to display them
I used the Promise.all method and it worked like a charm, thanks!
1

Try this

useEffect(()=>{
    
    const fetchSpecificTypes = async ()=>{
       const promises=specificTypes.map(type=>fetch(type.url).then(res=>res.json()));
       const response=await Promise.all(promises)
       setMovies(response.flatMap(c=>c)); 
    } 

    fetchSpecificTypes();

  },[]);

4 Comments

does not work TypeError: response[0].concat is not a function
I am using a different example than this now .
code updated.. please check..let me know if it doesnt work
using the example below yours I managed to get the movies now I am trying to display them correctly . Thank you very much for your time nevetheless .
1

The reason you do not receive any values from useFetch (assuming there are values to return) is that you return the structure { movies: [...] } but you read the non existent member movieTypes.

Change

const {movieTypes} = useFetch();

to

const {movies: movieTypes} = useFetch();

Declaring component state and effects from a function outside the comp looks quiet suspicious to me. Both for clarity and possibly for functionality, I think it is better to declare the state and any effects at the top level of the component. (If needed, the implementation of the effect rather than the declaration could be split out for reuse.) For example, the combined component:

import { React, useState, useEffect } from 'react'
import '../App.css';

export default function MovieList() {
    const key = process.env.REACT_APP_API_KEY;
    const specificTypes = [
        { 'Type': 'Top Rated', 'url': `https://api.themoviedb.org/3/movie/top_rated?api_key=${key}` },
        { 'Type': 'Trending', 'url': `https://api.themoviedb.org/3/trending/movie/day?api_key=${key}` },
    ];

    const [movieTypes, setMovieTypes] = useState([]);

    useEffect(() => {
        const fetchSpecificTypes = async () => Promise.all(specificTypes.map(type => fetch(type.url)
            .then(r => r.json())
            .then(r => r.results)
        )).then(results => {
            console.log(results);
            setMovieTypes([].concat(...results))
        }).catch(e => console.error(e));

        fetchSpecificTypes();
    }, []);

    const baseImgUrl = "https://image.tmdb.org/t/p";
    const size = "/w400";
    return (
        <div className="movie-list">
            {
                movieTypes.map(movie => {
                    let path = baseImgUrl + size + movie.backdrop_path;
                    return <div className="movie-item" key={movie.id}>
                        <img alt="movie" src={path} />
                        <h4 className="movie-overview">{movie.overview}</h4>
                        <p className="movie-title">{movie.title}</p>
                    </div>
                })
            }
        </div>
    );
}

Note that the fetch request was restructured to propagate the promise rather than to repeatedly await the promise. When all the fetch promises resolve the array is flattened and the state variable is set. If an error occurs it is reported. I haven't run the code, but I think it captures the idea.

1 Comment

That was just a typo I changed my code . I now return {movieTypes} and the problem remains

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.