简体   繁体   中英

Undefined getting value from fetch request in a JSON file using REACT.JS

I'm coming from a Vue environment I'm a bit confused with this, I read some other question similar to this but I couldn't make it work,

why I can't echo out the value of a nested object getting from a fetch request?

I console.log after setState and I got the values but in the render is undefined,

import React, { Component } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends Component {
  constructor() {
    super();
    this.state = {
      isLoading: true,
      articles: {}
    };
  }

  componentDidMount() {
    this.setState({ loading: true });
    fetch("./articles.json")
      .then(response => response.json())
      .then(result => {
        this.setState({
          isLoading: false,
          article: result.blog.article
        });
        console.log(
          "componentDidMount__this.state.article=",
          this.state.article.link.title
        ); //this gets the value
      })
      .catch(error => {
        console.error(error);
      });
  }

  render() {
    //let articleTitle;
    // this gets error ----> console.log(this.state.article.link.title);
    // because .link is undefined

    // console.log(this.state.article);
    // if (this.state.article !== "undefined") {
    //   console.log("wait what?..");
    // if I changed the state in fetch why this stil
    //   articleTitle = this.state.article.link.title;
    // } else {
    //   articleTitle = "";
    // }

    // I assign "this.state.article.link.title" to a variable so I can avoid the error,
    //
    return (
      <div className="App">
        {/*<h1>{articleTitle}</h1> */}
        <h1>{this.state.article.link.title}</h1>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

articles.json

{
  "blog": {
    "article": {
      "id": 1,
      "title": " 1 lorem ipsum",
      "description": "lorem ipsum",
      "image": {
        "desktop": "https://via.placeholder.com/900x500?text=desktop",
        "tablet": "https://via.placeholder.com/600x400?text=tablet",
        "mobile": "https://via.placeholder.com/320x320?text=mobile"
      },
      "link": {
        "title": "lorem link",
        "url": "#"
      },
      "author": {
        "avatar": "https://via.placeholder.com/125x125?text=125x125",
        "name": "lorem ipsum"
      }
    }
  }
}

https://codesandbox.io/s/wo65w21kl5

You have to put check before using dynamic states in render as it is called on both component mount and update.

This should work fine:

{this.state.isLoading ? '' : this.state.article.link.title}

It appears that this.state.article.link.title is being referenced when this.state.article === undefined .

The solution is to retrieve this.state.article.link.title in a safer manner.

This is typically achieved by leveraging short-circuit evaluation . I've also used destructuring assignment and default parameters in the example below.

Assigning default values to this.state is also recommended, especially when dealing with indeterminate data.

// Default `this.state`.
this.state = {
  article: {link: {title: ''}},
  articles: {},
  isLoading: true,
}

// Safe retrieval of `title`.
const {article = {}} = this.state
const {link = {}} = article.link
const title = link.title || ''

TRY this

   import React, { Component } from "react";
    import ReactDOM from "react-dom";

    import "./styles.css";

    class App extends Component {
      constructor() {
        super();
      }
      state = {
        isLoading: true,
        articles: {}
      };
      componentDidMount() {
        this.setState({ loading: true });
        fetch("./articles.json")
          .then(response => response.json())
          .then(result => {
            this.setState({
              isLoading: false,
              articles: result.blog.article
            });
          })
          .catch(error => {
            console.error(error);
          });
      }

      render() {
        let Test = this.state.articles ? (
          <div className="App">
            <h1>{this.state.articles.title}</h1>
          </div>
        ) : null;

        console.log(this.state.articles.title);
        return <div>{Test}</div>;
      }
    }

    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);

It seems that render method is invoked three times before fetch method finish, so in render method this.state.articles is empty object. You also wonders why guy from the tutorial does not have this issue, well in tutorial you mentioned object: this.state.character.name was used and in your code this.state.articles.link.title . This is the difference, because it is acceptable to use this.state.character.name (it refers to property from empty object so it will return undefined, whereas your this.state.article.link.title (it tries to access to property on object that does not exist). You can check it in console:

const obj = {};
console.log(obj.property); //undefined
console.log(obj.link.title); // Uncaught TypeError: Cannot read property 'title' of undefined

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM