Damini Ganesh
Damini Ganesh

Reputation: 308

data from second fetch API not available in the component

I am trying to render a component that gets the data from Django rest framework using the fetch() call. I have to use fetch twice because of the way my data is structured.

When I am trying to setState after I receive the data and pass it to a component and console.log it, I get empty braces {} and when I expand it I have data but I am not able to access it. But if I expand it I can see the data

example: {} Hotel: 123, Office: 456

data.Hotel = undefined

normally when I console.log a data it looks something like {Hotel:123, Office:456}

Is the way I have structured react wrong or am I missing something? I am new to react so any help would be appreciated.

DataProvider.js

import React, { Component } from "react";
import PropTypes from "prop-types";
class DataProvider extends Component {
  static propTypes = {
   endpoint: PropTypes.string.isRequired,
 };

  state = {
      project: {},
      run_set: [],
      project_eui: 0,
      run_euis: {},
      loaded: false,
      placeholder: "Loading.."
    };

  componentDidMount() {
  let currentComponent = this;
  var project = {}
  var run_set = {}
  var project_eui = 0
  var run_euis = {}
  var run_sets = []
  try {
    const data = {username: 'username', password: 'password'};
    fetch('URL', {
      method: 'POST',
      headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
      },
      body: JSON.stringify(data)
    }).then(function(response) {
      return response.json();
    }).then(function(data){
      return data.token
    }).then(function(token){
      try {
        fetch(currentComponent.props.endpoint, {
          method: 'GET',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'authorization': 'Token ' + token
          }
        }).then(function(response) {
          return response.json();
        }).then(function(project){
          project = project
          project_eui = project.eui


          var numBuildingType = project.run_set.length
          for (let i = 0; i < numBuildingType; i++) {
            try {
               //URL from 1st fetch call
              fetch(project.run_set[i], {
                method: 'GET',
                headers: {
                  'Accept': 'application/json',
                  'Content-Type': 'application/json',
                  'authorization': 'Token ' + token
                }
              }).then(function(response) {
                return response.json();
              }).then(function(run_set){
                run_sets.push(run_set)
                run_euis[run_set.building_type] = run_set.eui
              })
            } catch (e) {
              console.log(e);
            }
          }
        })
      } catch (e) {
        console.log(e);
      }
    })
  } catch (e) {
    console.log(e);
  }
  currentComponent.setState({ project: project, project_eui: project_eui, run_set: run_sets, run_euis: run_euis, loaded: true });
}
  render() {
    const { project, project_eui, run_set, run_euis, loaded, placeholder } = this.state
    if (loaded == true) {
      const myJson = { "project": this.state.project, "run_set": this.state.run_set, "project_eui": this.state.project_eui, "run_euis": this.state.run_euis}
      return this.props.render(myJson)
    } else {
      return <p> {placeholder} </p>
    }
  }
}

StructureData.js


...

const StructureData = ({ data }) => {
  console.log(data.run_euis) --------------------> {}
  return (
  <div>
    <p>something</p>
  </div>
);
}

export default StructureData;

App.js

...
return <DataProvider endpoint= {projectApiEndpoint}
                       render= {data => <StructureData data={data} />}
                      />
...

Upvotes: 0

Views: 46

Answers (1)

jered
jered

Reputation: 11581

currentComponent.setState({ project: project, project_eui: project_eui, run_set: run_sets, run_euis: run_euis, loaded: true }); is in the wrong place.

You start a very complicated asynchronous Promise chain which involves a fetch. That code will take some time to complete. You can't use setState synchronously immediately after, because your fetch code is still in-flight. Your setState call needs to be inside of your fetch's successful resulting .then, maybe here?

              //URL from 1st fetch call
              fetch(project.run_set[i], {
                method: 'GET',
                headers: {
                  'Accept': 'application/json',
                  'Content-Type': 'application/json',
                  'authorization': 'Token ' + token
                }
              }).then(function(response) {
                return response.json();
              }).then(function(run_set){
                run_sets.push(run_set)
                run_euis[run_set.building_type] = run_set.eui
                currentComponent.setState({ project: project, project_eui: project_eui, run_set: run_sets, run_euis: run_euis, loaded: true });
              })

The behavior you're seeing in console is due to the fact that when you log an object to the console, what you see is its value at the moment that it was logged. In this case you create an object variable, start a fetch call, and then log the object - but at that moment the object is still empty. However, by the time you click the "expand" arrow in the console, it will re-evaluate the object and show you its new current value - which happens to be after the fetch has completed. Using console.log for debugging can be prone to weird behavior like that; I recommend you get used to using the debugger tools. https://medium.freecodecamp.org/mutating-objects-what-will-be-logged-in-the-console-ffb24e241e07

Upvotes: 1

Related Questions