Dave.Q
Dave.Q

Reputation: 399

Can't access array data from API call when using map() in React

I'm attempting to map over data I received from an API call. Getting shallow endpoints works fine, but anything nested gives me an error.

The goal is to get all of the opening themes and display them in a 'ul'.

The exact error "TypeError: anime.opening_themes is undefined"

Repo to the project

Heres the endpoints.

enter image description here

Heres my component.

const AnimeDetails = (props) => {
  const API = 'https://api.jikan.moe/v3/anime'

  const initialState = {
    anime: []
  }

  const [anime, setAnime] = useState(initialState)

  useEffect(() => {
    const getAnime = async () => {
      const response = await fetch(`${API}/${props.match.params.animeId}`)
      const data = await response.json()

      console.log(data);
      setAnime(data) // set initial state to hold data from our API call
    }
    getAnime()
  }, []) // [] prevents useEffect from running in an infinite loop

  return (
    <AnimeDetailsWrapper>
      <Title>{anime.title}</Title>
      <Details>
        {anime.opening_themes
          .map((song, index) => (
            <li key={index}>{song}</li>
          ))}
      </Details>
    </AnimeDetailsWrapper>
  )
}

Upvotes: 2

Views: 1518

Answers (3)

Abhi Patel
Abhi Patel

Reputation: 224

you should try like this

first, remove initialState code and use direct code like following

if the response is in the form of an array

const [anime, setAnime] = useState([])

if the response is in the form of an object

const [anime, setAnime] = useState({})

otherwise, null will work with any response

const [anime, setAnime] = useState(null)

return code like this

 return (
  <>  {anime && <AnimeDetailsWrapper>
      <Title>{anime.title}</Title>
      <Details>
        {anime.opening_themes
          .map((song, index) => (
            <li key={index}>{song}</li>
          ))}
      </Details>
    </AnimeDetailsWrapper>}</>
  )

Upvotes: 0

Chris Heald
Chris Heald

Reputation: 62688

Your initial state is an empty array, not an empty object:

const initialState = {
  anime: []
}

When your component mounts, there is no data yet, so you're attempting to render [].opening_themes.map, and obviously there is no opening_themes property on an empty array.

Set your initial state to an empty object instead:

const initialState = {}

And you will probably want to test that you have data before attempting to render it:

return anime.mal_id && (
    <AnimeDetailsWrapper>
      <Title>{anime.title}</Title>
      <Details>
        {anime.opening_themes
          .map((song, index) => (
            <li key={index}>{song}</li>
          ))}
      </Details>
    </AnimeDetailsWrapper>
  )

This will prevent your component from rendering anything until your anime state contains a mal_id property.

Upvotes: 2

Hamza El Aoutar
Hamza El Aoutar

Reputation: 5667

The first time you render your component, the state anime is equal to {anime: []}, which has no property called opening_themes.

Upvotes: 0

Related Questions