JD Fill
JD Fill

Reputation: 402

Uncaught TypeError: this.state.map is not a function

In React, I'm using Axios to map an array to output a list of movie names that are pulling from the MovieDB API. It connects to the MovieDB just fine, however, I get the following error in the console:

Uncaught TypeError: this.state.movies.map is not a function

I believe this is preventing the movie list from outputting to the browser.

Codesandbox link is here.

Here's the SearchBar component where the code lies:

    import React, { Component } from "react";
    import TextField from "@material-ui/core/TextField";
    import axios from "axios";

    import "../SearchBar/_search-bar.scss";

    class SearchBar extends Component {
      state = {
        userSearchTerm: "",
        movies: []
      };

      // When user types, match the value to state
      onInputChange = e => {
        this.setState({ userSearchTerm: e.target.value });
      };

      // On submitting the input, grab the API
      onInputSubmit = e => {
        e.preventDefault();

        const movieName = this.state.userSearchTerm;
        const KEY = "XXXXXXXXXX";

        const searchQuery = `https://api.themoviedb.org/3/search/movie?api_key=${KEY}&language=en-US&query=${movieName}&page=10`;

        axios.get(searchQuery).then(res => {
          this.setState({ movies: res.data });
        });
      };

      render() {
        return (
          <div>
            <form onSubmit={this.onInputSubmit}>
              <TextField
                label="Search for a movie and hit enter..."
                margin="normal"
                className="search-bar"
                onChange={this.onInputChange}
              />
            </form>
            <ul>
              {this.state.movies.map(movie => (
                <li key={movie.id}>{movie.results.title}</li>
              ))}
            </ul>
          </div>
        );
      }
    }

    export default SearchBar;

On a side note, I tested out this same code, but with a different API and it worked just fine. Is there something wrong with the API itself or the this.state.movies.map?

Upvotes: 0

Views: 5327

Answers (3)

jdunham
jdunham

Reputation: 439

The API you are using is returning an object with "results" being the key you are looking for. If you update your setState to this.setState({ movies: res.data.results }); you should get what you are looking for.

Axios Response Schema

As a side note I would guard your map function with something like {Array.isArray(this.state.movies) && this.state.movies.map(movie => (... this will conditionally render the output only once movies is set in state and is an array.

Upvotes: 4

NN796
NN796

Reputation: 1267

A working code:

import React, { Component } from "react";
import TextField from "@material-ui/core/TextField";
import axios from "axios";

import "../SearchBar/_search-bar.scss";

class SearchBar extends Component {
  state = {
    userSearchTerm: "",
    movies: []
  };

  // When user types, match the value to state
  onInputChange = e => {
    this.setState({ userSearchTerm: e.target.value });
  };

  // On submitting the input, grab the API
  onInputSubmit = e => {
    e.preventDefault();

    const movieName = this.state.userSearchTerm;
    const KEY = "XXXXXX";

    const searchQuery = 'https://api.themoviedb.org/3/search/movie?api_key=${KEY}&language=en-US&query=${movieName}&page=10';

    axios.get(searchQuery).then(res => {
      console.log("res is ------", res)
      this.setState({ movies: res.data.results });
    });
  };

  render() {
    return (
      <div>
        <form onSubmit={this.onInputSubmit}>
          <TextField
            label="Search for a movie and hit enter..."
            margin="normal"
            className="search-bar"
            onChange={this.onInputChange}
          />
        </form>
        <ul>
          {this.state.movies.map(movie => (
            <li key={movie.id}>{movie.original_title}</li>
          ))}
        </ul>
      </div>
    );
  }
}

export default SearchBar;

You should use this.setState({ movies: res.data.results }); and <li key={movie.id}>{movie.original_title}</li>

Let me know if it works.

Upvotes: 3


You need to change the setState call to this:
this.setState({ movies: res.data.results });

The response is not an array, it is an object like this:

{
  "page": 10,
  "total_results": 136643,
  "total_pages": 6833,
  "results": [
    {
      "vote_count": 110,
      "id": 13189,
      "video": false,
      "vote_average": 7.3,
      "title": "A Christmas Carol",
      "popularity": 6.52,
      "poster_path": "/m3T3iLdE6J5PrqvvP0XNHBvM2bm.jpg",
      "original_language": "en",
      "original_title": "A Christmas Carol",
      "genre_ids": [
        18,
        10751,
        14,
        10770
      ],
      "backdrop_path": "/gaTpxTYQMGoagtMVYK8F7SjqTGM.jpg",
      "adult": false,
      "overview": "An old bitter miser who makes excuses for his uncaring nature learns real compassion when three ghosts visit him on Christmas Eve.",
      "release_date": "1984-12-17"
    },
    {
      "vote_count": 419,
      "id": 12103,
      "video": false,
      "vote_average": 6.1,
      "title": "Don't Say a Word",
      "popularity": 9.995,
      "poster_path": "/qx3hgW9MqxsEEFjx4eSbpp1Fe2l.jpg",
      "original_language": "en",
      "original_title": "Don't Say a Word",
      "genre_ids": [
        53
      ],
      "backdrop_path": "/AaOtoMzqWJPSNXPRKwbvqf6MbKo.jpg",
      "adult": false,
      "overview": "When the daughter of a psychiatrist is kidnapped, he's horrified to discover that the abductors' demand is that he break through to a post traumatic stress disorder suffering young woman who knows a secret..",
      "release_date": "2001-09-28"
    }
  ]
}

Upvotes: 0

Related Questions