Wie kann man das Rendern von Daten richtig zeitlich einstellen?

8

Ich versuche, Daten aus Open Data abzurufen, um eine schnelle Heatmap zusammenzustellen. Dabei möchte ich einige Statistiken hinzufügen. Fast alles läuft gut, da ich die Daten habe und die Karte rendern kann, aber ich bin mir nicht sicher, wie ich mit Berechnungen umgehen soll, wenn ich die Daten erhalte, da es einige Zeit dauert, bis Daten eingehen. Wie richte ich die Dinge so ein? Ich kann eine Funktion für eine Statusvariable ausführen, wenn sie noch nicht unbedingt Daten empfangen hat. Derzeit erhalte ich eine Null als Nummer, die als Requisiten an StatCard übergeben wird.

Unten sind meine Versuche:

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;

Reparaturversuch

   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();

  }
LoF10
quelle
Ist this.setState({ data: res, loading: true})ein Tippfehler in der requestDataFunktion? sollte nicht loadingeingestellt werden, falsewenn die Daten abgerufen werden?
Thgaskell
Ja, es ist ein Tippfehler
LoF10
Sind Sie sicher, dass die Verwendung von Abfrageparametern der API-Dokumentation entspricht?
Jurrian
Ja, wie Sie im Code sehen können, werden die Daten abgerufen, das ist nicht das Problem. Ich habe die Daten. Das Problem ist, dass ich nicht sicher bin, wo und wann calculatorInspections () ausgeführt werden soll, da es ausgeführt werden muss, wenn ich alle API-Daten erhalten habe und dann als Requisiten an die statCard übergeben werde.
LoF10
Bitte senden Sie mir den Codesandbox Link
TopW3

Antworten:

0

Rendern <StatCard />Sie nur, wenn Sie die Daten haben, die Sie benötigen:

{this.state.totalInspections && <StatCard totalInspections={this.state.totalInspections} /> }
Chase DeAnda
quelle
Ich habe das versucht, aber keine Würfel. Es wurde auch versucht, die Berechnung nur auszuführen, wenn this.state.data in componentDidMount vorhanden ist, aber kein Glück. Was ich jetzt bekomme, ist nur die Nummer 0, wo die StatCard sein würde
LoF10
1
Ich verstehe, weil Sie nicht verstehen, wie Versprechen / asynchrones Verhalten funktionieren. Sie müssen await requestData()es nicht direkt zur Funktion requestData hinzufügen und hinzufügen async fetchData = () => {}. Besser noch, fetchDatasollte nur die Daten abrufen und im Zustand speichern. Dann sollten Sie verwenden componentDidUpdate, um calculateInspections()nur bei this.state.dataÄnderungen auszulösen
Chase DeAnda
Hey Chase, ich versuche zu operationalisieren, worauf Sie hingewiesen haben, obwohl ich zugeben muss, dass ich noch lerne und nicht sicher bin, wie ich es genau implementieren soll. Ich habe meinen Versuch zu meiner Frage hinzugefügt, ob Sie einen Blick darauf werfen und mich in die richtige Richtung weisen könnten.
LoF10
0

Zunächst glaube ich nicht, dass Sie eine separate Funktion benötigen calculateInspections(). Sie können diese Logik in den thenRückruf einfügen.

fetchData = () => {
  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(data => {
      this.setState({
        data: data,
        loading: true,
        totalInspections: this.state.data.length
      })
    })
}

Zweitens ist die Einstellung this.state.totalInspectionseffektiv redundant, da Sie einfach Folgendes tun können:

{this.state.data && <StatCard totalInspections={this.state.data.length} /> }

Schließlich vermeiden , mit componentDidUpdate()Haken , wenn Sie neu zu reagieren. Meistens schießt du dir in den Fuß.

Derzeit hat Sie Ihre Reparaturversuche gerade in eine Endlos-Render-Schleife gebracht. Dies geschieht, weil bei jedem Aufruf nach dem Rendern der Lifecycle-Hook setState()aufgerufen wird componentDidUpdate(). Aber innerhalb componentDidUpdate()Ihres Anrufs wird erneut angerufen setState() , wodurch ein Folgeanruf für denselben Lebenszyklus-Hook ausgelöst wird, und somit geht die Schleife weiter und weiter.

Wenn Sie die Faustregel verwenden componentDidUpdate()und nach setState()innen rufen müssen , stellen Sie immer eine Stoppbedingung vor. In Ihrem Fall wird es sein:

componentDidUpdate () {
  if (this.state.data) {
    if (this.state.totalInspections !== this.state.data.length) {
      this.setState({ totalInspections: this.state.data.length })
    }
  }
}
Hackape
quelle
0

Ihr Problem ist also, dass der Status isLoading vor asynchronen Aufrufen SYNCHRON gesetzt werden muss.

Also in Ihrem componentDidMount:

componentDidMount() {
    try {
      this.setState({ loading: true }); // YOU NEED TO SET TRUE HERE
      this.fetchData();
    } catch (err) {
      console.log(err);
      this.setState({
        loading: false
      })
    }
}

Dies stellt das Laden sicher, sobald Sie den Anruf tätigen. Dann wird Ihr Anruf getätigt und dieser Teil ist ASYNCHRON. Sobald die Daten eingehen, ist das Laden abgeschlossen

.then(data => {
  this.setState({
    data: data,
    loading: false, // THIS NEEDS TO BE FALSE
    totalInspections: this.state.data.length
  })
})

Darüber hinaus: Ihre Render-Methode kann mehrere return-Anweisungen haben. Geben Sie anstelle von bedingtem jsx Ihr Ladelayout zurück

render() {

    if (this.state.loading) {
        return <div> I am loading </div>
    }

    return <div> Proper Content </div>;

}
Daniel Duong
quelle
0

Hier ist meine Lösung.

class App extends Component {

    constructor(props) {
        super(props);
        this.state = {
            data: [],
            dateInput: '2019-10-01',
            loading: false,
            error: false
        };
    }

    async componentDidMount() {
        try {
            await this.fetchData(this.state.dateInput);
        } catch (err) {
            this.setState({ loading: false, error: true });
        }
    }

    fetchData = (date) => new Promise(resolve => {
        this.setState({ loading: true });
        fetch(`https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=latitude > 39 AND latitude< 45 AND inspection_date >= '${date}'&$limit=50000`)
            .then(res => res.json())
            .then(res => {
                this.setState({ data: res, loading: false, error: false });
                resolve(res.data);
            });
    })

    handleDateInput = e => {
        this.setState({ dateInput: e.target.value }) //update state with the new date value
        this.fetchData(e.target.value);
    }

    render() {
        const { loading, data } = this.state;
        return (
            <div>
                {loading && (
                    <div className={classes.splash_screen}>
                        <div className={classes.loader}></div>
                    </div>
                )}
                {data && <StatCard totalInspections={data.length} />}
                <Dates handleDateInput={this.handleDateInput} />
                <Leaf data={data} />
            </div>
        );
    }
}

TopW3
quelle