2

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>
  )
}
3
  • Looks like you're trying to array-destructure an object...? Commented Oct 30, 2019 at 1:00
  • Can you please provide an animeId. I will check from my side. Commented Oct 30, 2019 at 2:31
  • I've edited the question and added a link to the repo. Commented Oct 30, 2019 at 15:57

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

5 Comments

You beat me to it.
The initial state is not an empty array, but an object that has a property anime which is an array
@Dave.Q notice the return anime.mal_id && ( which prevents the rendering if the anime is not yet received from the endpoint. If it's still fail, please provide a minimal reproducible example in your question.
I've added the repo to the question.
@Dave.Q an external reference to the repo is tolerated, but only if the relevant code is in the question. Also, the repo is not up to date with the code in your question, so it's rather useless...
0

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.