1

I am doing a React.js project. I am fetching data from an API with different endpoints. I have a component where I pass data from an endpoint of this API by props from its parent. Also, on that component I am calling the rest of the endpoints. I want to be able to use all the values that match the props (first API call). The issue is that the first API call includes the values of the other endpoints, but only the url. So I did a function lastNumber (that doesn't work) in order to let both values match. Also, that function needs to be able to call number with more than one digit and I do not know how to do it. For instance, the las character of the string of those url could be 1,2 or 10, 12,20 etc. So, I need to be able to use the "people number". Maybe there is a different approach. This is the whole component:

import { useEffect, useState } from "react";    
import styles from './MovieDetail.module.css';

const MovieDetail = ({films}) => {
    const [results, setResults] = useState([]);

    const urls = {
        people: "https://www.swapi.tech/api/people/",
        planets: "https://www.swapi.tech/api/planets/",
        starships: "https://www.swapi.tech/api/starships/",
        vehicles: "https://www.swapi.tech/api/vehicles/",
        species: "https://www.swapi.tech/api/species/"
    }
    
    
    const fetchApis = async () => {
      try {
        const responses = await Promise.all(Object.entries(urls).map(async ([ key, url ]) => {
          const res = await fetch(url)
          return [ key, (await res.json()).results ]
        }))
    
         return Object.fromEntries(responses)
      } catch (err) {
        console.error(err)
      }
    }
    
    useEffect(() => {
      fetchApis().then(setResults)
    }, [])
    console.log('results', results)

    const lastNumber = Number(films.properties.characters.slice(-1) - 1);
    

    return (
        <div className={styles.card}>
            <div className={styles.container}>
                <h2>{films.properties?.title}</h2>
                <p>{films.description}</p>
                <p>Release date: {films.properties?.release_date}</p>
                <p>Director: {films.properties?.director}</p>
                <p>Producer: {films.properties?.producer}</p>
                <p>Characters: {films.properties?.producer}</p>
                <p>Characters:</p>
                {results.people[lastNumber].map(result => (
                    <ul>
                        <li>{result.properties.name}</li>
                    </ul>
                ))}
            </div>            
        </div>
    );
}
 
export default MovieDetail;

This is a sandbox link with the whole code.

UPDATE

A more practical explanation: In the API there are films, and in every one there is a list of characters (people) that worked on the movie. So, I would like to render the movie title, director etc and the list of characters that worked on the movie.

3
  • After looking into the implementation, I guess you want to take the length of films.properties.characters array. So can't you just do ? const lastNumber = films.properties.characters.length; Commented Dec 22, 2021 at 9:22
  • Hi @DipanshKhandelwal that will give me the lenght of the array. If you check the API. There are films, an in every one there is a list of characters (people) that work on the movie. So, I would like to render the movie title, director etc and the list of characters that worked on the movie. Commented Dec 22, 2021 at 9:31
  • Because, they should match with the movie. In every movie, there is a number of people that worked, it's different on each one. For instance, in one movie it could be people/1 and in another not. So, it should match what people work on each film. That information it is on films.properties.characters or in the API url swapi.tech I am trying to learn how works fetch API's with multiple endpoints and how to handle them. Commented Dec 22, 2021 at 9:53

1 Answer 1

2

In MovieDetail, you are getting a film object, not an array of films? So you should start by renaming it film, so it is clearer that it is an object, not an array.

Then, you know that the film contains some information that you want, for instance the people that played in it. So you have to fetch their URLs, like this:

const peoples = film.properties.characters.map(characterURL => {
   // fetch the people through an API call 
   const people = (await (await fetch(characterURL)).json()).results
   return people
}

Then, in peoples, you have every people that played in the movie. So if you want to display a list of characters name, you can do like this:

{peoples.map(people=> (
    <ul>
        <li>{people.properties.name}</li>
    </ul>
))}

Does it answer your question ?

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks again The Tisiboth. Your logic sounds better than mine. I had an issue in the console about not using async, so I added it in the map before characterURL. However, the code doesn't run either for another error in console 'cannot read properties of undefined (reading 'properties')'. I updated the sandbox with your code already for you to see it.
I added a question mark on properties and it works. Thanks TheTisiboth! However, sometimes I get this error: 429 (Too Many Requests). I am trying to implement a setTimeOut to the function peoples, but unsuccesfully.
Yes I know why. But it would be easier to create a new post so you can ask this new question, as it is an other issue. Anyway, I am glad that I could help!
Why are you just calling .json on the fetch without any brackets etc. ? this will throw a TypeError: fetch(...).json is not a function
You are right, this is a typo

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.