TypeError:无法读取 JSONPlaceholder 未定义的属性“url”

Posted

技术标签:

【中文标题】TypeError:无法读取 JSONPlaceholder 未定义的属性“url”【英文标题】:TypeError: Cannot read property 'url' of undefined for JSONPlaceholder 【发布时间】:2020-07-24 19:18:36 【问题描述】:

我正在尝试从 jsonPlaceholder 加载图片。当我控制台注销图像数组时,它可以成功运行。当我尝试从元素调用 URL 时,它会引发 TypeError。我试着用异步的方式来做,即使这样也行不通。有什么解决方法吗?可能是什么问题?

import React, Component from 'react';
import axios from 'axios';

class Home extends Component



    state = 
                posts : [],
                images : []

            ;

    componentDidMount =  async () => 
        const res = await axios.get('https://jsonplaceholder.typicode.com/posts');
            this.setState(posts: res.data.slice(0,10))

        const res2 = await axios.get('https://jsonplaceholder.typicode.com/photos');
            this.setState(images: res2.data.slice(0,10))

        


    render = () =>     


        const posts = this.state.posts;
        const images = this.state.images;

        const PostList = this.state.posts.length ? (


            this.state.posts.map((post,index) => 
                return(
                    <div className = "post card" key = post.id>
                            <div className = "card-content">
                                <div className = "card-title">
                                    post.title
                                </div>
                                <div className>
                                    (this.state.images[index].url) //console.log(this.state.images[index] works, but .URL doesn't 
                                </div>
                                <div>
                                    <p>post.body</p>
                                </div>
                            </div>
                    </div>

                )
            )) : (<div className = "center">No Posts!</div> ) 

        return(
                <div className = "container">
                    <div className = "center">
                        PostList
                    </div>
                </div>)
    



export default Home;

【问题讨论】:

它是 thumbnailUrl,而不是 url 如果您是数组映射中的 console.log(this.state.images[index]),您每次都会得到什么 【参考方案1】:

问题存在于多个setState。一旦你设置了状态,它就会更新并呈现asynchronously。所以没有等待next setState。最好在一次调用中设置两个数据。

componentDidMount =  async () => 
        const res = await axios.get('https://jsonplaceholder.typicode.com/posts');
            this.setState(posts: res.data.slice(0,10))

        const res2 = await axios.get('https://jsonplaceholder.typicode.com/photos');
            this.setState(images: res2.data.slice(0,10))

        

您可以像这样清理代码:

工作岗位: https://codesandbox.io/s/articlepodcast-wd2rv?file=/home.js:0-1214

import React,  Component  from "react";
import axios from "axios";

class Home extends Component 
  constructor(props) 
    super(props);
    this.state = 
      posts: [],
      images: []
    ;
  

  componentDidMount = async () => 
    const res = await axios.get("https://jsonplaceholder.typicode.com/posts");
    const res2 = await axios.get("https://jsonplaceholder.typicode.com/photos");
    this.setState(
      posts: res.data.slice(0, 10),
      images: res2.data.slice(0, 10)
    );
  ;

  render = () => 
    const  posts, images  = this.state;
    const PostList = posts.length ? (
      posts.map((post, index) => 
        return (
          <div className="post card" key=post.id>
            <div className="card-content">
              <div className="card-title">post.title</div>
              <div className>images[index].url </div>
              <div>
                <p>post.body</p>
              </div>
            </div>
          </div>
        );
      )
    ) : (
      <div className="center">No Posts!</div>
    );

    return (
      <div className="container">
        <div className="center">PostList</div>
      </div>
    );
  ;


export default Home;

【讨论】:

【参考方案2】:

由于您的第一个 setState,它会在第二个异步等待之前进行渲染。

const res = await axios.get('https://jsonplaceholder.typicode.com/posts');
const res2 = await axios.get('https://jsonplaceholder.typicode.com/photos');

this.setState(images: res2.data.slice(0,10), posts: res.data.slice(0,10))

像这样试试。

最好有这样的东西;

this.setState(
   posts: res.data.slice(0,10).map((post, index) => (
     ...post,
    image: res2.data.slice(0,10)[index]

   ))

)

【讨论】:

【参考方案3】:

问题

由于componentDidMount 函数中设置状态的方式而发生错误。为了分解为什么会发生这种情况,我将解释代码试图做什么: 首先,Axios 将发送一个 get 请求 来获取帖子,因为这是一个正在等待的异步操作,当获取数据时,javascript 将处理其他事情。因此它将继续并第一次运行render 函数。渲染函数将返回No Posts!,因为this.state.posts.length ?false

然后当我们完成获取帖子时,componentDidMount 将继续,在这种情况下,它将更新状态 this.setState(posts: res.data.slice(0,10)),每次调用 setState 时,它都会告诉 react 为该组件运行 render 函数再次确保页面反映了我们的状态。然后我们进入下一行等待获取照片,而我们正在等待render 函数将第二次运行,因为(正如刚才所说)我们改变了组件的状态。在这个时间点,我们仍然没有照片,因为我们正在等待它们,但是我们确实有帖子,所以检查我们是否有帖子 this.state.posts.length ? 将是 true。然后当我们到达images[index] 时,它将是未定义的,因为我们还没有完成获取照片并将它们设置为状态。 (我希望这可以帮助您理解问题)。

解决方案

在我们获得所有数据后更新状态,以便render 函数第二次运行我们有图像:

const res = await axios.get("https://jsonplaceholder.typicode.com/posts");
const res2 = await axios.get("https://jsonplaceholder.typicode.com/photos");
this.setState(
  posts: res.data.slice(0, 10),
  images: res2.data.slice(0, 10)
);

我还建议向this.state.images 添加安全检查,就像您对帖子所做的那样,以确保您在尝试使用之前确实拥有数据。对于来自网络调用的所有数据,这是一个很好的做法,因为在尝试从服务获取数据时可能会发生许多意想不到的事情。

最后,我还建议您尝试使用 chrome 调试器跟踪您的代码。这将帮助您了解代码正在做什么,以便更好地理解为什么会发生这样的问题。这是一个很好的指南,说明如何做到这一点。 https://developers.google.com/web/tools/chrome-devtools/javascript

【讨论】:

以上是关于TypeError:无法读取 JSONPlaceholder 未定义的属性“url”的主要内容,如果未能解决你的问题,请参考以下文章

TypeError:无法读取未定义的属性(读取“国家”)

TypeError:无法读取未定义的属性(读取“名称”)

TypeError:无法读取属性“集合”[重复]

TypeError:无法读取 null 的属性(读取“发射”)

TypeError:无法读取未定义的属性(读取“匹配”):

TypeError:无法读取未定义的属性(读取“createContext”)