React - Help with movieDB API

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

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

BTW @camperextraordinaire, 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?

@gunhoo93, @camperextraordinaire, 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.

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().

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>
    );
  }

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.

1 Like

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

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.

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?

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

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?

This is very helpful, thank you.

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? @camperextraordinaire @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.

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.

1 Like

Hey, I’m wondering if there is a way that I can show a total of movies per page?, I would like to display, let’s say, 200 movies all at once. Why? Because I think it’d be easier for me to implement pagination this way, besides I can con control the amount of movies with only 1 querystring. I’m reading the documentation but I can’t find anything about it. I also don’t know where you found out about the &page=2 thing (which works)

edit: well after reading some questions in the forums it seems like it’s not possible…

@camperextraordinaire

Keeping the above in mind, one way to implement pagination would be saving the querystring in a variable and make the page=1 dynamic?, i.e.; queryString${pageNumber} and then increment the pageNumber every time the next button is clicked, the opposite for previous button.

The problem I see with this is that if I have numbers in between the prev and next button I would have to write plenty of if else statements, if button number 2 is clicked, then pageNumber is set equal to 2, etc.

How would you go about this?

You can simply do the JavaScript loop over the json returned by the TMDB API like in this tutorial.