Shivam Yadav
Shivam Yadav

Reputation: 47

Not able to fetch data from server in my ReactJs site

Getting undefined data type error while fetching data from JSON

I have searched at many places but didn't get the suitable answer

import SavedData from "./SavedData";

export default class Saved extends React.Component {
  constructor() {
    super();
    this.state = {
      loading: true,
      datas: [],
    };
  }

  async componentDidMount() {
    const url = "https://todo-list-site.herokuapp.com/todo-data";
    const response = await fetch(url);
    const todoData = response.json().then((res) => {
      this.setState({ datas: res });
    });
  }

  render() {
    console.log(this.state.datas[0].description);   //not able to get data
    return (
      <div>
        {/* {this.state.datas.map((items) => (
          <SavedData
            key={items.countTodo}
            title={items.title}
            desc={items.desc}
          />
        ))} */}
      </div>
    );
  }
}

Someone help me so that I can proceed

Upvotes: 0

Views: 54

Answers (2)

justarandomguy
justarandomguy

Reputation: 331

Just like Dave Newton has pointed out in the comments, the render is triggered before the request completes. This is normal and you just need to handle it properly.

If you see the console logs of this codesandbox, you can see that initially this.state.datas is just an empty array [] - so any attempt to access this.state.datas[0].description will be undefined. Only after the state is updated when the request completes, the logs show the data retrieved - this is because according to the mount lifecycle of a React Component, the render() is called before the componentDidMount() and also the request being async.

This is very common and it is even recommended by the official React docs to make HTTP calls in componentDidMount(). The docs also has provided an example to handle this issue.

import SavedData from "./SavedData";

export default class Saved extends React.Component {
  constructor() {
    super();
    this.state = {
      loading: true,  // we initially set this to true
      datas: [],
    };
  }

  async componentDidMount() {
    const url = "https://todo-list-site.herokuapp.com/todo-data";
    const response = await fetch(url);
    const todoData = response.json().then((res) => {
      this.setState({
        datas: res,
        loading: false  // when the request is complete, we set this to false
      });
    });
  }

  render() {
    if (this.state.loading) {
      // during the first render, loading will be true and we
      // can return a loading message or a spinner
      return (
        <div>Loading...</div>
      );
    }

    // when render is called after the state update, loading will be false
    // and this.state.datas will have the fetched data
    console.log(this.state.datas[0].description);
    return (
      <div>
        {this.state.datas.map((items) => (
          <SavedData
            key={items.countTodo}
            title={items.title}
            desc={items.desc}
          />
        ))}
      </div>
    );
  }
}

Upvotes: 1

Rob Terrell
Rob Terrell

Reputation: 2562

Your datas state is initially an empty array until your componentDidMount fires and sets the state. As a result, your console log will then be undefined until the state is set. In order to combat this you must wait for this.state.datas[0] to be true before accessing the first objects description within the array. The following code seems to work as expected

import React from "react";

export default class Saved extends React.Component {
  constructor() {
    super();
    this.state = {
      loading: true,
      datas: []
    };
  }

  async componentDidMount() {
    const url = "https://todo-list-site.herokuapp.com/todo-data";
    const response = await fetch(url);
    response.json().then((res) => {
      this.setState({ datas: res });
    });
  }

  render() {
    console.log(this.state.datas[0] && this.state.datas[0].description);
    return (
      <div>
        {this.state.datas.map((items, i) => (
          <div key={i}>
            <div> title={items.title}</div>
            <div> desc={items.description}</div>
          </div>
        ))}
      </div>
    );
  }
}

Upvotes: 0

Related Questions