1

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

Marcogomesr
  • 2,614
  • 3
  • 30
  • 41
  • 1
    Possible duplicate of [setState doesn't update the state immediately](https://stackoverflow.com/questions/41278385/setstate-doesnt-update-the-state-immediately) – Panther Feb 26 '19 at 10:30
  • it's a totally different question...also the answer wont solve my problem... I dont need to console.log in a callback...I can console.log and see the data...the problem is that is render undefined – Marcogomesr Feb 26 '19 at 10:38
  • updated codesandbox [https://codesandbox.io/s/ryo89x6lp](https://codesandbox.io/s/ryo89x6lp).. in react component, the methods are called in the following order when an instance of a component is being created and inserted into the DOM `1. constructor()` `2. static getDerivedStateFromProps()` `3. render()` `4. componentDidMount()` ie, the `render` method executed before the `componentDidMount` method.. – Ashokbharathi Feb 26 '19 at 10:54

4 Answers4

0

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}

Rajender Joshi
  • 4,155
  • 1
  • 23
  • 39
0

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 || ''
Arman Charan
  • 5,669
  • 2
  • 22
  • 32
0

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);
pixellab
  • 588
  • 3
  • 12
0

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
Oskar
  • 41
  • 4