Pascual
Pascual

Reputation: 13

Catch error films.map is not a function in React

I get the catch error or type error on

{films.map((film, i) => { ...

can not read proper 'map' or undefined from the Rest Api swapi in React.

  1. build id url to show information
  2. Create the component
  3. State to add api data
  4. Binding the function fetchData to this state
  5. Call to api function characters
  6. Call to api function movies
  7. Life circle to call the function
  8. Rendering the component
  9. Bring the data of the character to the data to show them
  10. mapping the list
const api_url_films = 'https://swapi.dev/api/films/'

class Card extends Component {
  constructor(props) {
    super(props)
    this.state = { 
      films: []
    }
    this.fetchDataFilms = this.fetchDataFilms.bind(this) 
  }

  fetchDataFilms(){ 
    fetch(api_url_films)
      .then(data => (data.json()))
      .then(results => {
        const api_data = results
        this.setState({
          films: api_data
        })
      })
  }

  componentDidMount() { 
    this.fetchDataFilms()
  }

  render() { 
    const {films} = this.state

    return(
      <div>
        {films.map((film, i) => {
          return (  
            <li key={i}>{film}</li>
          )
        }
        )}
      </div>
    )
  }
}

I try to add in a unordered list also but doesn't work thanks

Upvotes: 0

Views: 194

Answers (2)

Drew Reese
Drew Reese

Reputation: 202864

The response has this shape:

{
  count: number,
  next: any,
  previous: any,
  results: Object[], // <-- this is what you want
}

Update the handler to access into the response object and save the results array.

fetchDataFilms(){ 
  fetch(api_url_films)
    .then(data => (data.json()))
    .then(results => {
      const api_data = results
      this.setState({
        films: api_data.results, // <-- access the results array
      })
    })
}

Additionally, the film objects you are mapping are still object, so they are not valid React children. You will need to access specific properties.

{films.map((film) => {
  return (  
    <li key={film.title}>{film.title}</li> // or director, or producer, etc...
  )
}

Upvotes: 1

LeeMinHo
LeeMinHo

Reputation: 318

I think html is rendered before data is fetched although the films variable is an empty array in initial state.

When films variable is set from fetching function, html will be re-rendered.

try this code. This component will show an empty box until film data is fetched.

hope this works!

render() { 
    let films = this.state.films

    if(films == null || films == []) return <></>
    return(
      <div>
        {films.map((film, i) => {
          return (  
            <li key={i}>{film}</li>
          )
        }
        )}
      </div>
    )
  }

Upvotes: 1

Related Questions