最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - TypeError: Cannot read property 'data' of undefined - can't access Object "props&a

programmeradmin2浏览0评论

I'm making an API call in via axios in React with a UseEffect.
We set the response to a variable called data using useState

const [data, setData] = useState({});
  setData(response);

The response is from NASA API and we only get one object (pasted below) returned for this call.

Since I named the response "data" and it has a "data" key as well, if I want to log the url, I know I would type console.log(data.data.url) and that works smoothly in my app.js main function. In my card.js ponent, I can successfully log console.log(data) and console.log(data.data) and it gives exactly what you would expect, but when I console.log(data.data.url) or (data.data.title) it bees for some reason undefined, so then this results in a big error in the return function of JSX and the site won't load:

 TypeError: Cannot read property 'data' of undefined error.

I don't think I'm doing anything wrong with my naming as it works fine at higher levels in the object e.g. console.log(data.data) works and I see before my eyes the next level properties listed.

I am literally console.logging this:

{console.log('FROM INSIDE THE RETURN')}
{console.log(props.data)}  // works, displays object {}
{console.log(props.data.data)}  //works, displays object one level lower   
{console.log(props.data.data.url)}  // type error. You name the property.

Needless to say this doesn't work, which was my first approach to the assignment:

<img src={props.data.data.url}/>

That said we got the program working with help of the team lead by shaving off the top layer of the object upstream as follows:

SetData(response.data)

// as opposed to 
SetData(response)

// and then using 
<img src={props.data.url}/>

So we didn't have to reach to the bottom in the props, but for clarity, I want to know why and what difference it makes to the piler, particularly when it worked fine up to n-1 layers, where n is the number of layers of the object.

I even changed the name of one of the data variables so 'data' wasn't duplicated and the behavior was the same.

Thank you for your help and insights! I really appreciate any insights you can share as well as feedback on my question.

Here is the object I'm working with.

     {
        data: {
            copyright: "Bryan Goff",
            date: "2020-03-18",
            explanation: "What's happening behind...[truncated]...Florida, USA.",
            hdurl: ".jpg",
            media_type: "image",
            service_version: "v1",
            title: "Anticrepuscular Rays over Florida",
            url: ".jpg"
        },
        status: 200,
        statusText: "OK",
        headers: {
            contenttype: "application/json"
        },
        config: {
            url: "",
            method: "get",
            headers: {
                Accept: "application/json, text/plain, */*"
            },
            transformRequest: [
                null
            ],
            transformResponse: [
                null
            ],
            timeout: 0,
            xsrfCookieName: "XSRF-TOKEN",
            xsrfHeaderName: "X-XSRF-TOKEN",
            maxContentLength: -1
        },
        request: {}
    }

I'm making an API call in via axios in React with a UseEffect.
We set the response to a variable called data using useState

const [data, setData] = useState({});
  setData(response);

The response is from NASA API and we only get one object (pasted below) returned for this call.

Since I named the response "data" and it has a "data" key as well, if I want to log the url, I know I would type console.log(data.data.url) and that works smoothly in my app.js main function. In my card.js ponent, I can successfully log console.log(data) and console.log(data.data) and it gives exactly what you would expect, but when I console.log(data.data.url) or (data.data.title) it bees for some reason undefined, so then this results in a big error in the return function of JSX and the site won't load:

 TypeError: Cannot read property 'data' of undefined error.

I don't think I'm doing anything wrong with my naming as it works fine at higher levels in the object e.g. console.log(data.data) works and I see before my eyes the next level properties listed.

I am literally console.logging this:

{console.log('FROM INSIDE THE RETURN')}
{console.log(props.data)}  // works, displays object {}
{console.log(props.data.data)}  //works, displays object one level lower   
{console.log(props.data.data.url)}  // type error. You name the property.

Needless to say this doesn't work, which was my first approach to the assignment:

<img src={props.data.data.url}/>

That said we got the program working with help of the team lead by shaving off the top layer of the object upstream as follows:

SetData(response.data)

// as opposed to 
SetData(response)

// and then using 
<img src={props.data.url}/>

So we didn't have to reach to the bottom in the props, but for clarity, I want to know why and what difference it makes to the piler, particularly when it worked fine up to n-1 layers, where n is the number of layers of the object.

I even changed the name of one of the data variables so 'data' wasn't duplicated and the behavior was the same.

Thank you for your help and insights! I really appreciate any insights you can share as well as feedback on my question.

Here is the object I'm working with.

     {
        data: {
            copyright: "Bryan Goff",
            date: "2020-03-18",
            explanation: "What's happening behind...[truncated]...Florida, USA.",
            hdurl: "https://apod.nasa.gov/apod/image/2003/AntiCrepRays_Goff_3072.jpg",
            media_type: "image",
            service_version: "v1",
            title: "Anticrepuscular Rays over Florida",
            url: "https://apod.nasa.gov/apod/image/2003/AntiCrepRays_Goff_960.jpg"
        },
        status: 200,
        statusText: "OK",
        headers: {
            contenttype: "application/json"
        },
        config: {
            url: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY",
            method: "get",
            headers: {
                Accept: "application/json, text/plain, */*"
            },
            transformRequest: [
                null
            ],
            transformResponse: [
                null
            ],
            timeout: 0,
            xsrfCookieName: "XSRF-TOKEN",
            xsrfHeaderName: "X-XSRF-TOKEN",
            maxContentLength: -1
        },
        request: {}
    }
Share Improve this question edited Mar 19, 2020 at 9:35 MwamiTovi 2,52219 silver badges26 bronze badges asked Mar 19, 2020 at 3:57 gcrgcr 4632 gold badges7 silver badges18 bronze badges 5
  • recreate this issue in minimum steps via stackblitz. – Praveen Soni Commented Mar 19, 2020 at 4:18
  • minimal reproducible example may help others better understanding what you are facing – keikai Commented Mar 19, 2020 at 4:29
  • My code is here github./webbuildermn/nasa-photo-of-the-day/tree/Gerald-Ryan (note it is on this branch only) Forked from: github./LambdaSchool/nasa-photo-of-the-day – gcr Commented Mar 19, 2020 at 15:53
  • Here it is on sb: stackblitz./github/xoriwpoqv The two files to pare are app.js and card.js. Toggle on and off line 13 of card.js and pare the difference in the console. I see now that it calls Card twice. I don't know why or if that has anything to do with the bug. – gcr Commented Mar 19, 2020 at 16:26
  • 1 This is the correct stackblitz link to reproduce the issue stackblitz./edit/github-rrx7ex – gcr Commented Mar 19, 2020 at 16:41
Add a ment  | 

2 Answers 2

Reset to default 2

This is indeed an interesting challenge.
Let's do step-by-step analysis and see if we'll agree:

// this initializes `data = {}` when the app first launches
const [data, setData] = useState({});

// Chances are, you are using this within the "useEffect"
// If so at that point, the above `data = response`
setData(response)

You are most likely making the axios NASA API call within the useEffect.
So then, let's narrow down to the API call.

API calls are often asynchronous (non-blocking).
In other words, this data fetching process doesn't block your client-side from doing other "activities". With that out of the way, let square back to your shared code:

Explanation 1: It might be an occurrence while we are fetching data

// works, because initially "data = {}"
{console.log(props.data)}

// works, displays object one level lower
{console.log(props.data.data)}
// Explaining this...
// APIs are often backend apps that query a database for actual data. 
// This returned data is stored in "literals" (often arrays/lists/objects).

// type error. You name the property.
{console.log(props.data.data.url)}
// Based on the above explanation, 
// despite the second `data` being an Object literal, 
// "url" isn't yet defined since the API is still "querying" the database

Explanation 2: It might be a namespace conflict

// If all is fine based on "explanation 1", 
// then this could be a "namespace" conflict during pilation.

// At pilation, JS finds two variables named "data"
// 1. The initial data value, 
   data = {}
// 2. The returned data key,
   {
     data: {...},
   }
// If we had a returned response as follows:
   results = {
     data: {...},
   }
// we probably would have something like this working 
{console.log(response.data.result.data.url)}

// And this might explains why these work...
{console.log(response.data.url)}
<img src={props.data.url}/>

Remember, we are dealing with stubborn JavaScript here.
And that's possibly why many big Reactjs projects increasing now involve TypeScript.

My guess is the api call is taking some time and you are trying to set the values before the api call returns . Please try to use a additional isLoading state to check if the api is still executing

import React from 'react';

const Component = () => {  
const [isLoading,setIsLoading] = useState(true)
const [data, setData] = useState({});

useEffect(()=>{
  setTimeout(()=>fetch('https://jsonplaceholder.typicode./users/1')
    .then(response => response.json())
    .then(json => {        
        setData(json)
      setIsLoading(false)        
    }),1000)

},[0])


return(
  isLoading ? 'Loading...' :
    <div>
      <h1>Hello {data.name}!</h1>
      <p>Your username is {data.username}</p>
    </div>
  )
}

export default Component

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论