【问题标题】:TypeError: Cannot read property 'url' of undefined for JSONPlaceholderTypeError:无法读取 JSONPlaceholder 未定义的属性“url”
【发布时间】: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]),您每次都会得到什么

标签: javascript json reactjs react-redux react-router


【解决方案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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-08-17
        • 1970-01-01
        • 2020-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-24
        相关资源
        最近更新 更多