React - Help with movieDB API

React - Help with movieDB API
0

#1

I am trying to build a search movie app with React using the movieDB api, all of this is fairly new to me, so please bear with me.

I don’t know if it’s just me, but I think the movieDB api documentation is not very clear, or they expect you to have some basic knowledge regarding query strings at least. In any case, I am just trying to get a list of movies sort by popularity:

https://api.themoviedb.org/3/search/movie?query=sort_by=popularity.desc&api_key={my_api_key}

This would just give me the following:

{
page: 1,
total_results: 0,
total_pages: 1,
results: [ ]
}

What is wrong with my query string, why am I getting an empty object?

On the other hand, the following query string works just fine:

https://api.themoviedb.org/3/search/movie?query=avengers&api_key={my_api_key}

Any help is greatly appreciated!

edit: a link to the movieDB api documentation: https://www.themoviedb.org/documentation/api/discover


#2

In the first querystring, you are not specifying the name of a movie after query=, yet you are using the search endpoint. Try using the discovery endpoint:

https://api.themoviedb.org/3/discovery/movie?sort_by=popularity.desc&api_key={my_api_key}

#3

Oh yeah this works, thank you. Just a minor fix, it would be “discover” not “discovery”.


#4

Yeah I meant discover, but was typing fast. :wink:


#5

BTW @randelldawson, can I have some guidance building this app? It’s my very first React project, I will probably need some help, should I just post my questions in this same post or just open a new one?


#6

@gunhoo93, @randelldawson, how can I get rid of the empty array show in the console:

This is the code:

class Movies extends Component {
  state = {
    movies: []
  };

  async componentDidMount() {
    const { data: movies } = await axios.get(
      "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=my_api_key"
    );
    this.setState({ movies });
    console.log(movies);
  }

  render() {
    console.log(this.state.movies);
    return (
      <div className="container">
        {/* {this.state.movies.results.map(movie => <h1>{movie.title}</h1>)} */}
      </div>
    );
  }
}

Notice that I had to comment out the map function in the render method because I was getting an error, I know this is happening because I am getting undefined or an empty array first as shown in the console.


#7

Why do you want to remove it and what do you want to replace it with?

This is happening because render() will trigger before componentDidMount().


#8

Because I want to render a list of the movies title into the DOM, but I’m getting an error, I think this error is because of undefined.

render() {
    console.log(this.state.movies);
    return (
      <div className="container">
         {this.state.movies.results.map(movie => <h1>{movie.title}</h1>)} 
      </div>
    );
  }

#9

When you initially render, movie state is not available, so you should deal with this case in your render(). If you have learned conditional rendering, try putting a spinner to indicate data is being loaded. Making that empty array disappear won’t be the solution.


#10

Umm I see. Yes I have learned conditional rendering, one question though. Would that be the only way to deal with this problem?


#11

I couldn’t think of any other solution on my end.
Since you have to wait for the data, why not just display something useful in the meanwhile?

You could do something like these, but they are just plain bad…

  • Adding initial fake data won’t solve the issue because it is fake data and will soon be replaced with real data.
  • Rendering after data is ready is meaningless because users won’t see anything while the data is being loaded.

#12

Something still off, I am not sure why I am getting an error when I try to do this:

class Movies extends Component {
  state = {
    movies: []
  };

  async componentDidMount() {
    const { data: movies } = await axios.get(
      "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=my_api_key"
    );
    this.setState({ movies });
    console.log(movies.results[0].title);
  }

  render() {
    // console.log(this.state.movies.results[0].title);
    return (
      <div className="container">
      </div>
    );
  }
}

I mean, the console.log in the componentDidMount() method works as expected, but if I uncomment the one in the render method, then I get an error. Is this happening because movies state is initially undefined?


#13

NVM, I fixed my bug:

class Movies extends Component {
  state = {
    movies: []
  };

  async componentDidMount() {
    const { data: movies } = await axios.get(
      "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=my-api-key"
    );
    this.setState({ movies: movies.results });
  }

  render() {
    console.log(this.state.movies);
    return (
      <div className="container">
        {this.state.movies.map(movie => <h1 key={movie.id}>{movie.title}</h1>)}
      </div>
    );
  }
}

Basically my bug was when I was updating the state. Should I still use conditional rendering? Everything seems to load pretty fast, but that might change when I add images and more stuff


#14

Whenever you pull data from an API, you always have to plan for the situation that the api’s site might have a heavy load (which could make getting data take longer) or that the api is down (it can happen), or a change is made to the api which causes your queries to fail (similar to the site being down). Your app should deal gracefully with all these situations.


#15

Understood, that’s very good to know. I do have another question though, it’s related to the api, honestly I can’t find none of it in the documentation, so I’m lost.

How can I move to page 2 to show more results?


#16

Add &page=2 to the querystring. In the api response you get a total_pages property, so you will know the max number of pages you can go to. It also shows you the page number you have received, so you can always know where you are in the pagination.


#17

Not sure if you have read through it yet, but there is extension documentation here.

Also, there is a forum for the api.


#18

This is very helpful, thank you.


#19

I want to display the genre of each movie in the UI, I am mapping through an array of objects that looks like this:

const movies = [
  {
    vote_count: 294,
    id: 439079,
    video: false,
    vote_average: 6.1,
    title: "The Nun",
    popularity: 245.792,
    poster_path: "/sFC1ElvoKGdHJIWRpNB3xWJ9lJA.jpg",
    original_language: "en",
    original_title: "The Nun",
    genre_ids: [27, 9648, 53],
    backdrop_path: "/fgsHxz21B27hOOqQBiw9L6yWcM7.jpg",
    adult: false
  },
  {
    vote_count: 7962,
    id: 299536,
    video: false,
    vote_average: 8.3,
    title: "Avengers: Infinity War",
    popularity: 210.234,
    poster_path: "/7WsyChQLEftFiDOVTGkv3hFpyyt.jpg",
    original_language: "en",
    original_title: "Avengers: Infinity War",
    genre_ids: [12, 878, 28],
    backdrop_path: "/lmZFxXgJE3vgrciwuDib0N8CfQo.jpg",
    adult: false
  },
  {
    vote_count: 87,
    id: 346910,
    video: false,
    vote_average: 5.6,
    title: "The Predator",
    popularity: 165.197,
    poster_path: "/wMq9kQXTeQCHUZOG4fAe5cAxyUA.jpg",
    original_language: "en",
    original_title: "The Predator",
    genre_ids: [27, 878, 28],
    backdrop_path: "/x0VXCWSTny5JRvpgDnw5ptwQyhA.jpg",
    adult: false
  }
];

As you can see, each object has an array of genres_ids, so I am just displaying numbers (for each item, the one with index of 0, so the first id) I also have another array of objects listing all genres and its corresponding id:

const genres = [
  { id: 28, name: "Action" },
  { id: 12, name: "Adventure" },
  { id: 16, name: "Animation" },
  { id: 35, name: "Comedy" },
  { id: 80, name: "Crime" },
  { id: 99, name: "Documentary" },
  { id: 18, name: "Drama" },
  { id: 10751, name: "Family" },
  { id: 14, name: "Fantasy" },
  { id: 36, name: "History" },
  { id: 27, name: "Horror" },
  { id: 10402, name: "Music" },
  { id: 9648, name: "Mystery" },
  { id: 10749, name: "Romance" },
  { id: 878, name: "Science Fiction" },
  { id: 10770, name: "TV Movie" },
  { id: 53, name: "Thriller" },
  { id: 10752, name: "War" },
  { id: 37, name: "Western" }
];

My solution to display the genre name of each movie is as followed:

const getGenres = movies.map(movie => {
  if (movie.genre_ids[0] === 12) return "Adventure"
  else if (movie.genre_ids[0] === 27) return "Horror"
  else if (movie.genre_ids[0] === 28) return "Action"
});

This works, but I am sure there must be a better way to do this, it feels odd having to write one condition for each genre. If this was an array of 500 items, then my solution is no longer viable. How can I achieve the same result using a more viable code? @randelldawson @gunhoo93

I tried something like this:

const getGenres = movies.map(movie => {
	for (let i = 0; i < movies.length; i++) {
  	if (movie.genre_ids[0] === genres[i].id) return genres[i].name
  }
});

But I am getting undefined in index 0 and 2, not sure why.


#20

Well, first you are getting undefined because your .map() will sometimes return undefined. If you don’t want to change anything, you’d still have to return the original item.

One solution I can think of is building another hash-table-like structure that maps genre id to genre name.

Here is a demo:

Keep in mind this has no edge case handling.