JangoCG
JangoCG

Reputation: 976

Access nested state

I have the following object in my state and I'm trying to access the bracket and entries properties.

enter image description here

This is how I try to access the attributes, however I just get "cannot access attribut of undefined" errors

  function App () {
  const [ladder, setLadder] = useState({})

  useEffect(() => {
    async function fetchMyAPI () {
      try {
        const res = await axios.get(
          'data/wow/pvp-season/27/pvp-leaderboard/3v3?namespace=dynamic-eu&locale=en_US&access_token=12345'
        )
        setLadder(res)
      } catch (error) {
        console.log(error)
      }
    }
    fetchMyAPI()
  }, [])

  return (
    <>
      {console.log(ladder.data.bracket)} // cannot access bracket of undefined ...
      {console.log(ladder.data.entries[0])} // cannot access entries of undefined ...
    </>
  )
}

Upvotes: 0

Views: 39

Answers (2)

SomoKRoceS
SomoKRoceS

Reputation: 3043

You're getting that error because on the first render (which occur on load up of the component), the state variable ladder is an empy object. Only after the data is fetched, the variable is updated. So You can "render nothing" as long as ladder.data is undefined by checking the value before accessing it:

  function App () {
  const [ladder, setLadder] = useState()

  useEffect(() => {
    async function fetchMyAPI () {
      try {
        const res = await axios.get(
          'data/wow/pvp-season/27/pvp-leaderboard/3v3?namespace=dynamic-eu&locale=en_US&access_token=12345'
        )
        setLadder(res)
      } catch (error) {
        console.log(error)
      }
    }
    fetchMyAPI()
  }, [])

  return (
    <>
      {console.log(ladder && ladder.data.bracket)}
      {console.log(ladder && ladder.data.entries[0])}
    </>
  )
}

Notice that I've changed the initial value to undefined instead of {}. If you are willing to remain the initial value an empty object for some reason, you can use lodash (for instance) to check if the object is empty before rendering:

const _ = require('lodash');

  return (
    <>
      {console.log(!_.isEmpty(ladder) && ladder.data.bracket)}
      {console.log(!_.isEmpty(ladder) && ladder.data.entries[0])}
    </>
  )

Upvotes: 1

Al Hill
Al Hill

Reputation: 479

return (
    <>
      {console.log(ladder?.data?.bracket)}
      {console.log(ladder?.data?.entries[0])}
    </>
  )

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

You can also use the logical AND operator

return (
    <>
      {console.log(ladder && ladder.data &&  ladder.data.bracket)}
      {console.log(ladder && ladder.data && Array.isArray(ladder.data.entries) && ladder.data.entries[0])}
    </>
  )

You can also adapt the initial value of ladder and let the render code unchanged

const [ladder, setLadder] = useState({
  data: {
    entries: []
  }
})

Or even lodash get function!

return (
    <>
      {console.log(_.get(ladder, "data.bracket"))}
      {console.log(_.get(ladder, "data.entries[0]"))}
    </>
  )

Upvotes: 1

Related Questions