Как полностью завершить получение данных и присвоить их состоянию компонента, прежде чем продолжить в componentDidMount React? - PullRequest
0 голосов
/ 08 января 2019

Мне нужно выполнить несколько запросов на выборку. Основываясь на моем первом запросе, который я сделал, чтобы сделать несколько других запросов после получения всего, я должен иметь возможность назначить данные состоянию реагирующего компонента. Похоже, что я присваиваю значения состоянию компонента до того, как метод выборки завершен, и, следовательно, они выглядят как пустой массив.

Я попытался удалить метод внутренней выборки снаружи и выполнить запрос.

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]

Я ожидаю, что конечные состояния компонентов будут выглядеть следующим образом или в функции рендеринга - логирование консоли должно выглядеть следующим образом.

devices = [{},{},{},{},{}...]
dataPoints = [[{},{},{},..], [{},{},{},..], [{},{},{},..], ....]

Ответы [ 3 ]

0 голосов
/ 08 января 2019

Я думаю, причина в dataPoint.concat(data), array.concat возвращает новый массив, это неизменяемая функция. Чтобы решить проблему, попробуйте: dataPoint = dataPoint.concat(data)

0 голосов
/ 08 января 2019

ваш код const dataPoints = this.state.dataPoints.concat(dataPoint) внутри карты всегда будет содержать пустой массив, поскольку fetch является асинхронным, а ваш dataPoint получит значение только после вызовов API.

Другая проблема: dataPoint.concat(data) concat возвращает новый массив, но вы не сохраняете новый массив, который вы можете использовать dataPoint = dataPoint.concat(data) или dataPoint = [...dataPoint, ...data]

Вам нужно дождаться результата вызовов API до const dataPoints = this.state.dataPoints.concat(dataPoint). Вы можете использовать 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);
}
0 голосов
/ 08 января 2019

Обычным шаблоном React будет установка флага загрузки в вашем состоянии и отображение загрузчика (или возврата нуля), пока страница не загружена.

Конструктор:

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

В вашем компоненте DidMount: (упрощенно)

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

В вашей функции рендеринга

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

  // Here render when data is available
}

ВАЖНОЕ ПРИМЕЧАНИЕ:

В вашей функции componentDidMount вы выполняете 2 setState. Вы должны сделать только один, чтобы предотвратить ненужные повторные показы.

В вашем примере удалите первый

  this.setState({
     devices : data
  })

И объединить оба в конце вместо this.setState({ dataPoints, devices: data });

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...