开发者问题收集

TypeError:无法读取未定义的属性“数据”-无法访问 Reactjs 中超出特定级别的对象“props”

2020-03-19
13264

我正在通过 React 中的 axios 使用 UseEffect 进行 API 调用。
我们使用 useState

const [data, setData] = useState({});
  setData(response);
将响应设置为名为 data 的变量。>

响应来自 NASA API,我们只获得此调用返回的一个对象(粘贴在下面)。

由于我将响应命名为“data”,并且它也有一个“data”键,如果我想记录网址,我知道我会输入 console.log(data.data.url) ,这在我的 app.js 主函数中运行顺利。在我的 card.js 组件中,我可以成功记录 console.log(data)console.log(data.data) ,它给出了您所期望的结果,但是当我 console.log(data.data.url)(data.data.title) 时,它由于某种原因变为 undefined ,因此这会导致 JSX 的返回函数出现大错误,并且网站无法加载:

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

我认为我的命名没有错,因为它在对象的更高级别上运行良好,例如 console.log(data.data) 可以运行,并且我眼前列出了下一级属性。

我实际上是在 console.logging 这个:

{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.

不用说,这不起作用,这是我完成作业的第一个方法:

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

也就是说,我们在团队负责人的帮助下通过削减对象上游的顶层使程序正常运行,如下所示:

SetData(response.data)

// as opposed to 
SetData(response)

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

所以我们不必到达道具的底部,但为了清楚起见,我想知道为什么以及它对编译器有什么不同,特别是当它在 n-1 层之前工作正常时,其中 n 是对象的层数。

我甚至更改了其中一个数据变量的名称,因此“数据”没有重复,行为也是一样的。

感谢您的帮助和见解!我非常感谢您分享的任何见解以及对我的问题的反馈。

这是我正在处理的对象。

{
        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: {}
    }

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)

您最有可能在 useEffect 中进行 axios NASA API 调用。
那么,让我们缩小到 API 调用。

API 调用通常是异步的(非阻塞)。
换句话说,这个数据获取过程不会阻止您的客户端执行其他“活动”。解决了这个问题后,让我们回到您的共享代码:

解释 1:这可能是我们在获取数据时发生的事件

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

解释 2:这可能是命名空间冲突

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

// At compilation, 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}/>

请记住,我们在这里处理的是顽固的 JavaScript。
这可能就是为什么现在许多大型 Reactjs 项目都涉及 TypeScript

MwamiTovi
2020-03-19

我猜想 api 调用需要一些时间,而您正尝试在 api 调用返回之前设置值。请尝试使用额外的 isLoading 状态来检查 api 是否仍在执行

import React from 'react';

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

useEffect(()=>{
  setTimeout(()=>fetch('https://jsonplaceholder.typicode.com/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
muddassir
2020-03-19