Search code examples
javascriptreactjsobjectundefined

How to access fetched json values in a react app


This is my first post on the platform. I hope It will be clear.

I'm working on a react movie app using tmdb API. The fetch works and returns an array of 20 objects (movies). I can map the array access the value of all movies, but I can't do the same for one specific movie.

For example I can display all the posters, but if I wanna access just one poster or any value from one specific movie like id, I get:

Uncaught TypeError: Cannot read properties of undefined (reading 'id').

I notice in the console that when I log movieList[1] it runs 4 times. Twice it returns undefined then returns the object twice.

The Movie react component

import React, { useEffect, useState } from 'react';
import tmdb from '../api/tmdb';

function Movie() {

    const [movieList, setMovieList] = useState([])
    
    useEffect(() => {
      const getMovieList = async () => {
        const {data} = await tmdb.get('/discover/movie?')
        setMovieList(data.results)
      }
      getMovieList()
    },[])

    console.log(movieList) // this works
    console.log(movieList[1]) // this works
    console.log(movieList[1].id) // this returns "Uncaught TypeError: Cannot read properties of undefined (reading 'id')"

  return (
    <div>

        {/* this works and renders a list of posters */}

        {movieList.map((movie) => (
          <img alt={""} style={{width:"300px", marginLeft:"10px", marginTop:"10px"}} src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}/>
          ))}


        {/* this returns 'Uncaught TypeError: Cannot read properties of undefined (reading 'id')' */}

        <img alt={""} style={{width:"300px", marginLeft:"10px", marginTop:"10px"}} src={`https://image.tmdb.org/t/p/w500${movieList[1].poster_path}`}/>
    </div>
  )
}


export default Movie

The fetch component:

import axios from 'axios';

export default axios.create({
    baseURL: 'http://api.themoviedb.org/3',
    headers: {
        accept: "application/json"
    },
    params: {
        api_key:'key here'
    }
})

Example of movie object

{
    "adult": false,
    "backdrop_path": "/rMvPXy8PUjj1o8o1pzgQbdNCsvj.jpg",
    "genre_ids": [
        28,
        12,
        53
    ],
    "id": 299054,
    "original_language": "en",
    "original_title": "Expend4bles",
    "overview": "Armed with every weapon they can get their hands on and the skills to use them, The Expendables are the world’s last line of defense and the team that gets called when all other options are off the table. But new team members with new styles and tactics are going to give “new blood” a whole new meaning.",
    "popularity": 1489.424,
    "poster_path": "/mOX5O6JjCUWtlYp5D8wajuQRVgy.jpg",
    "release_date": "2023-09-15",
    "title": "Expend4bles",
    "video": false,
    "vote_average": 6.3,
    "vote_count": 254
}

Solution

  • movieList is initially an empty array:

    const [movieList, setMovieList] = useState([])
    

    So what you're doing here is:

    console.log(movieList) // logging an empty array
    console.log(movieList[1]) // logging the second element in an empty array, which is "undefined"
    console.log(movieList[1].id) // trying to log the id property from that "undefined" element
    

    Since undefined has no properties, this will produce an error.

    At its simplest, you can just remove these console.log statements entirely. But if you want to keep them for debugging purposes, you need to account for the empty array. For example, you can use optional chaining:

    console.log(movieList);
    console.log(movieList[1]);
    console.log(movieList[1]?.id);
    

    Or perhaps wrap some in a conditional based on the length of the array:

    console.log(movieList);
    if (movieList.length > 1) {
      console.log(movieList[1]);
      console.log(movieList[1].id);
    }
    

    And for the JSX markup, just remove that <img> that's outside of the .map() operation. You're already showing all of the values for every element in the array inside the .map() operation, I see no need to show one specific one outside of it. But if you do want to for some reason, you'd have to check if the array has any elements first.

    But overall the point is that you can't log properties from the seond element (or any element) in an array that has zero elements.