I am attempting to pull data from Open Data to put together a quick heat map. In the process I want to add some stats. Almost everything runs well in that I have the data and am able to render the map, but I am unsure how to deal with calculations once I get the data since it takes time for data to come in. How do I set things up so that I can run a function on a state variable if it hasn't necessarily received data yet? Currently I am getting a null as the number that is passed as props to StatCard.
Below are my attempts:
App.js
  import React, { Component } from 'react';
import Leaf from './Leaf';
import Dates from './Dates';
import StatCard from './StatCard';
import classes from './app.module.css';
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data:[],
      cleanData:[],
      dateInput: '2019-10-01',
      loading: false,
      totalInspections: null,
      calculate: false
    };
  }
  componentDidMount() {
    try {
      this.fetchData();
    } catch (err) {
      console.log(err);
      this.setState({
        loading: false
      })
    }
  }
  fetchData=()=>{
    const requestData = async () => {
      await fetch(`https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=latitude > 39 AND latitude< 45 AND inspection_date >= '${this.state.dateInput}'&$limit=50000`)
        .then(res => res.json())
        .then(res =>
          //console.log(res)
          this.setState({ data: res, loading: true})
        )
    }
    const  calculateInspections = () => {
      this.setState({totalInspections: this.state.data.length})
    }
    //call the function
    requestData();
    if(this.state.data) {
      calculateInspections();
    }
  }
  handleDateInput = (e) => {
    console.log(e.target.value);
    this.setState({dateInput:e.target.value, loading: false}) //update state with the new date value
    this.updateData();
    //this.processGraph(e.target.value)
  }
  updateData =() => {
    this.fetchData();
  }
  LoadingMessage=()=> {
    return (
      <div className={classes.splash_screen}>
        <div className={classes.loader}></div>
      </div>
    );
  }
  //inspection_date >= '${this.state.dateInput}'& 
 // https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=inspection_date >= '2019-10-10T12:00:00' 
  render() {
    return (
      <div>
        <div>{!this.state.loading ? 
              this.LoadingMessage() : 
              <div></div>}
        </div>
        {this.state.totalInspections && <StatCard totalInspections={this.state.totalInspections} /> }
          <Dates handleDateInput={this.handleDateInput}/>
          <Leaf data={this.state.data} />
      </div>
    );
  }
}
export default App;
StatCard.js
import React from 'react';
const StatCard = ( props ) => {
    return (
        <div >
            { `Total Inspections: ${props.totalInspections}`}
        </div>
    )
};
export default StatCard;
Attempt Repair
   componentDidMount() {
    try {
      this.fetchData();
    } catch (err) {
      console.log(err);
      this.setState({
        loading: false
      })
    }
  }
  componentDidUpdate () {
    if(this.state.data) {
      this.setState({totalInspections: this.state.data.length})
    }
  }
  fetchData= async ()=>{
    const requestData = () => {
    fetch(`https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=latitude > 39 AND latitude< 45 AND inspection_date >= '${this.state.dateInput}'&$limit=50000`)
        .then(res => res.json())
        .then(res =>
          //console.log(res)
          this.setState({ data: res, loading: true})
        )
    }
    //call the function
    await requestData();
  }
                So your problem is that isLoading state needs to be set synchronously before any async calls.
So in your componentDidMount:
componentDidMount() {
    try {
      this.setState({ loading: true }); // YOU NEED TO SET TRUE HERE
      this.fetchData();
    } catch (err) {
      console.log(err);
      this.setState({
        loading: false
      })
    }
}
This ensures loading as soon as you make the call. Then your call is made and that part is asynchronous. As soon as data comes through, the loading is done:
.then(data => {
  this.setState({
    data: data,
    loading: false, // THIS NEEDS TO BE FALSE
    totalInspections: this.state.data.length
  })
})
Furthermore, your render method can have multiple return statements. Instead of having conditional JSX, return your loading layout:
render() {
    if (this.state.loading) {
        return <div> I am loading </div>
    }
    return <div> Proper Content </div>;
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With