Search code examples
reactjsfetches6-promise

Pulling nested URL endpoints in ReactJS


I have been playing around with the Star Wars API and I'm having a hard time trying to pull in the Film data from the characters (https://swapi.dev/api/people/1/).

export const SwapiFilms = (props) => {
  const [films, setFilms] = useState([]);

  const filmList = props.filmsUrl;

  useEffect(() => {
    function getData() {
      try {
        const response = Promise.all(
          filmList.map((url) => 
            fetch(url).then((res) => res.json())
          )
        );
        console.log(response);
        setFilms(response);
      } catch (error) {
        console.log(error);
      }
    }

    getData();
  }, []);

  return (
    <>
      Title: {films.title}
      <br />
      <br />
      Director: {films.director}
      <br />
      <br />
      {JSON.stringify(films)}
    </>
  );
};

Solution

  • You should await the Promise.all.

    useEffect(() => {
      async function getData() { // <-- declare getData an async function
        try {
          const response = await Promise.all( // <-- await promise resolution
            filmList.map((url) => 
              fetch(url).then((res) => res.json())
            )
          );
          console.log(response);
          setFilms(response);
        } catch (error) {
          console.log(error);
        }
      }
    
      getData();
    }, []);
    

    You probably shouldn't mix promise chaining and async/await either. Stick with one or the other

    Promise Chain

    useEffect(() => {
      Promise.all(filmList.map(fetch))
        .then(responses => {
          if (!response.ok) {
            throw Error('Film response not ok!');
          }
          return response.json();
        })
        .then(films => {
          console.log(films);
          setFilms(films);
        })
        .catch(error => console.log(error));
    }, []);
    

    Async/Await

    useEffect(() => {
      async function getData() {
        try {
          const responses = await Promise.all(filmList.map(fetch));
          const films = await Promise.all(responses.map(res => res.json());
    
          console.log(films);
          setFilms(films);
        } catch (error) {
          console.log(error);
        }
      }
    
      getData();
    }, []);
    

    Side Note: your films state is an array, so you'll want to access films using array methods in your render return.