0

I have to perform multiple fetch queries. Based on my first query i have make to make a multiple other queries after receiving all the i should be able to assign the data to the react component state. It appears that i am assigning the values to the component state before the fetch method is finished and hence they appear empty array.

I have tried by removing the inner fetch method outside and performing the query.

import React, { Component } from 'react';
import './App.css';
import Sensors from './iot/Sensors';

class App extends Component {
  constructor (props) {
      super (props);
      this.state = {
        status: 'disconnected',
        devices: [],
        dataPoints: []
      };
  }

  componentDidMount() {
    // Get Zigbee devices
    fetch('http://localhost:3000/ssapi/zb/dev')
    .then((res) => res.json())
    .then((data) => {

      this.setState({
        devices : data
       })
      data.map((device) => {
        const dataPoint = []
        JSON.parse(device.metadata).dataGroups.map((datagroup) =>{
          const url = 'http://localhost:3000/ssapi/zb/dev/' + device.id + '/ldev/' +  datagroup.ldevKey + '/data/' + datagroup.dpKey;
          fetch(url)
          .then((res) => res.json())
          .then((data) =>{
            dataPoint.concat(data)
            console.log('Data', data);
            console.log('Inside dataPoint', dataPoint);
          })
          .catch((error) => console.log(error));
        }) // dataGroups.map
        console.log("Final dataPoint", dataPoint);
        const dataPoints = this.state.dataPoints.concat(dataPoint);
        this.setState({ dataPoints });
      }) // data.map

    }) // fetch
    .catch((error) => console.log(error));
  }

  render() {
    console.log('Render Devices', this.state.devices);
    console.log('Render dataPoints', this.state.dataPoints);
  }][1]][1]

I am expecting a final component states that look like this or in render function - console logging should look like this.

devices = [{},{},{},{},{}...]
dataPoints = [[{},{},{},..], [{},{},{},..], [{},{},{},..], ....]
BhanuKiran
  • 2,631
  • 3
  • 20
  • 36

3 Answers3

0

A common React pattern would be to set a loading flag in your state and display a loader (or return null) as long as the page is not loaded.

Constructor:

class App extends Component {
  constructor (props) {
    super (props);
    this.state = {
      status: 'disconnected',
      devices: [],
      dataPoints: [],
      loading: true
    };
  }
}

In your componentDidMount: (simplified)

componentDidMount() {
  fetch('http://localhost:3000/ssapi/zb/dev')
  .then((res) =>
    this.setState({data: res.json(), loading: false});
  )
}

In your render function

render() {
  if (this.state.loading) {
    return <div>Loading ... Please Wait.</div>;
  }

  // Here render when data is available
}

IMPORTANT NOTE:

In your componentDidMount function you are doing 2 setState. You should only do one to prevent unecessary rerenders.

In your example, remove the first

  this.setState({
     devices : data
  })

And combine both at the end instead this.setState({ dataPoints, devices: data });

klugjo
  • 19,422
  • 8
  • 57
  • 75
0

I thing the reason is in dataPoint.concat(data), array.concat returns a new array, it is an immutable function. To solve the proble just try: dataPoint = dataPoint.concat(data)

aquilesb
  • 2,182
  • 1
  • 19
  • 19
0

your code const dataPoints = this.state.dataPoints.concat(dataPoint) inside map will always concat empty array, because fetch is asynchronus and your dataPoint will only get value after api calls.

Other issue is dataPoint.concat(data) concat returns a new array but you are not storing the new array you can use dataPoint = dataPoint.concat(data) or dataPoint = [...dataPoint, ...data]

You need to wait for the result of api calls before const dataPoints = this.state.dataPoints.concat(dataPoint). You can use Promise.all

import React, { Component } from 'react';
import './App.css';
import Sensors from './iot/Sensors';

class App extends Component {
    constructor (props) {
    super (props);
    this.state = {
        status: 'disconnected',
        devices: [],
        dataPoints: []
    };
}

componentDidMount() {
    // Get Zigbee devices
    fetch('http://localhost:3000/ssapi/zb/dev')
    .then((res) => res.json())
    .then((data) => {
        this.setState({
            devices : data
        })
        //Using forEach instead of map because we don't need the return of map
        data.forEach((device) => {
            const urls = JSON.parse(device.metadata).dataGroups.map((datagroup) =>
                'http://localhost:3000/ssapi/zb/dev/' + device.id + '/ldev/' +  datagroup.ldevKey + '/data/' + datagroup.dpKey) // dataGroups.map
            Promise.all(urls.map(fetch))
            .then(responses => 
                Promise.all(responses.map(res => res.json()))
            )
            .then((data) =>{
                //This data will be array of responses of all fetch fired
                //destructuring response in array
                const dataPoint = data.reduce((acc, curr)=> acc.concat(curr),[]) 
                const dataPoints = this.state.dataPoints.concat(dataPoint)              
                console.log('All Data', data);
                console.log('Inside dataPoint', dataPoint);
                this.setState({ dataPoints });
            })
            .catch((error) => console.log(error));
        }) // data.map
    }) // fetch
    .catch((error) => console.log(error));
}

render() {
    console.log('Render Devices', this.state.devices);
    console.log('Render dataPoints', this.state.dataPoints);
}
Ashish
  • 4,206
  • 16
  • 45