¿Cómo cronometrar correctamente la representación de datos en reaccionar?

8

Estoy intentando extraer datos de Open Data para armar un mapa de calor rápido. En el proceso quiero agregar algunas estadísticas. Casi todo funciona bien porque tengo los datos y soy capaz de representar el mapa, pero no estoy seguro de cómo manejar los cálculos una vez que obtengo los datos, ya que lleva tiempo ingresarlos. ¿Cómo configuro las cosas para que ¿Puedo ejecutar una función en una variable de estado si aún no ha recibido necesariamente los datos? Actualmente obtengo un valor nulo como el número que se pasa como accesorios a StatCard.

A continuación están mis intentos:

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;

Intento de reparación

   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
fuente
¿Hay this.setState({ data: res, loading: true})un error tipográfico en la requestDatafunción? no loadingdebería establecerse en falsecuando se obtienen los datos?
thgaskell
Sí, es un error tipográfico
LoF10
¿Está seguro de que el uso de los parámetros de consulta se corresponde con la documentación de la API?
Jurrian
Sí, como puede ver en el código, se obtienen los datos, ese no es el problema. Tengo los datos El problema es que no estoy seguro de dónde y cuándo ejecutar CalculateInspections () porque debe ejecutarse cuando recibí todos los datos de la API y luego se pasaron como accesorios a la statCard.
LoF10
Por favor envíeme el enlace de
codesandbox

Respuestas:

0

Solo renderice <StatCard />si tiene los datos que necesita:

{this.state.totalInspections && <StatCard totalInspections={this.state.totalInspections} /> }
Chase DeAnda
fuente
Lo intenté pero sin dados. También intenté ejecutar el cálculo solo si this.state.data existe dentro de componentDidMount pero no hubo suerte. Lo que obtengo ahora es solo el número 0 donde sería la StatCard
LoF10
1
Ya veo, es porque no entiendes cómo funcionan las promesas / el comportamiento asíncrono. Necesita en await requestData()lugar de agregarlo directamente a la función requestData y agregar async fetchData = () => {}. Mejor aún, fetchDatasolo debe buscar los datos y almacenarlos en estado. Entonces debería usar componentDidUpdatepara disparar calculateInspections()solo cuando los this.state.datacambios
Chase DeAnda
Hola Chase, tratando de poner en práctica lo que has señalado, aunque admito que todavía estoy aprendiendo, así que no estoy seguro de cómo implementarlo exactamente. agregué mi intento a mi pregunta si podían echar un vistazo y señalarme en la dirección correcta.
LoF10
0

En primer lugar , no creo que necesites una función separada calculateInspections(). Puede poner esa lógica en la thendevolución de llamada.

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
      })
    })
}

En segundo lugar , la configuración this.state.totalInspectionses efectivamente redundante, ya que puede hacer:

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

Por último, evite usar el componentDidUpdate()gancho cuando sea nuevo en reaccionar. La mayoría de las veces terminas disparándote en el pie.

Actualmente, su intento de reparación acaba de meterlo en un bucle de renderizado infinito. Esto sucede porque cada vez que llama setState(), llamará a componentDidUpdate()gancho de ciclo de vida después de la representación. Pero dentro de componentDidUpdate()usted vuelve a llamar setState() , lo que induce una llamada de seguimiento al mismo enlace de ciclo de vida, y por lo tanto, el ciclo continúa.

Si debe usar componentDidUpdate()y llamar al setState()interior, regla general, siempre coloque una condición de detención por delante. En tu caso, será:

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

Entonces, su problema es que el estado de carga debe establecerse SINCRÓNICAMENTE antes de cualquier llamada asíncrona.

Entonces, en su componenteDidMount:

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

Esto garantiza la carga tan pronto como realice la llamada. Luego se realiza su llamada y esa parte es ASINCRÓNICA. Tan pronto como los datos lleguen, la carga estará lista.

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

Más aún: su método de representación puede tener múltiples declaraciones de retorno En lugar de tener jsx condicional, devuelva su diseño de carga

render() {

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

    return <div> Proper Content </div>;

}
Daniel Duong
fuente
0

Aquí está mi solución.

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
fuente