React:如何在渲染之前确保所请求的数据可用?

时间:2016-09-24 07:27:41

标签: javascript facebook reactjs

我试图用React创建一个天气小部件。

我从OpenWeatherMap获得的数据: http://openweathermap.org/api

我的问题是(或者是)一次调用render函数。但是,OpenWeatherMap API提供的请求数据还不可用。因此,解释器会抛出错误,因为访问了尚未定义的道具。

我找到了一个解决方法,将render函数的return语句放入if-else:



var React = require('react');
var DayInfos = require('./DayInfos.jsx');
var MainInfoPanel = require('./MainInfoPanel.jsx');

var WeatherApp = React.createClass({
  getInitialState: function() {
    return {    
      data: null
    }
  },
  componentWillMount: function() { // Call before the initial rendering
    var apiData = new Promise(function(resolve, reject) {
      var request = new XMLHttpRequest();

      request.onreadystatechange = function() {
        if (request.readyState === 4) {
          if (request.status === 200) {
            resolve(request.responseText);
          } else {
            reject('HTTP request on openweathermap has failed.');
          } 
        }
      }

      request.open('get', 'http://api.openweathermap.org/data/2.5/forecast?q=London,us&mode=json&appid=efcd313fa7a139f2fb20de306648eb8d');
      request.send();
    });

    apiData.then(function(weatherData) {
      this.setState({
        data: JSON.parse(weatherData)
      });
    }.bind(this), function(message) {
      console.log(message);
    });
  },
  render: function() {
    if (this.state.data) {  // Make sure the data is available.
      return (
        <div className="weather-app" >
          <MainInfoPanel city={ this.state.data.city } today={ this.state.data.list[0] } />
          <DayInfos />
          <DayInfos />
          <DayInfos />
          <DayInfos />
        </div>
      );
    } else {
      return null;
    }
  }
});

module.exports = WeatherApp;
&#13;
&#13;
&#13;

我现在工作,但我不确定我是否以正确的方式处理这个问题。

实际上我认为使用过的函数componentWillMount(https://facebook.github.io/react/docs/component-specs.html#mounting-componentwillmount)会解决这个问题。但显然它只会触发请求。其余的进一步运行而不等待数据到达。

因此我向更有经验的React开发人员提问:

在请求的数据可用之前,如何延迟初始渲染?

或者:

由于缺少数据而避免应用程序崩溃的正确策略是什么?

3 个答案:

答案 0 :(得分:3)

您需要将标志条件最初设置为false,并在数据到达时设置为true。

var React = require('react');
var DayInfos = require('./DayInfos.jsx');
var MainInfoPanel = require('./MainInfoPanel.jsx');

var WeatherApp = React.createClass({
  getInitialState: function() {
    return {    
      data: null,
      flag:false, //add flag
    }
  },
  componentWillMount: function() { // Call before the initial rendering
    var apiData = new Promise(function(resolve, reject) {
      var request = new XMLHttpRequest();

      request.onreadystatechange = function() {
        if (request.readyState === 4) {
          if (request.status === 200) {
            resolve(request.responseText);
          } else {
            reject('HTTP request on openweathermap has failed.');
          } 
        }
      }

      request.open('get', 'http://api.openweathermap.org/data/2.5/forecast?q=London,us&mode=json&appid=efcd313fa7a139f2fb20de306648eb8d');
      request.send();
    });

    apiData.then(function(weatherData) {
      this.setState({
        data: JSON.parse(weatherData),
        flag:true   // update state
      });
    }.bind(this), function(message) {
      console.log(message);
    });
  },
  render: function() {
    return flag ?   
        (
        <div className="weather-app" >
          <MainInfoPanel city={ this.state.data.city } today={ this.state.data.list[0] } />
          <DayInfos />
          <DayInfos />
          <DayInfos />
          <DayInfos />
        </div>
      )
    :
      null;
    }
  }
});

module.exports = WeatherApp

现在,如果您不想使用单独的变量,您也可以使用data.length属性进行跟踪。

但是在这里你需要为数据分配一个数组Array或Object,data = []或data = {}。然后你的渲染就像

 render: function() {
        return data.length  > 0 ?   
            (
            <div className="weather-app" >
              <MainInfoPanel city={ this.state.data.city } today={ this.state.data.list[0] } />
              <DayInfos />
              <DayInfos />
              <DayInfos />
              <DayInfos />
            </div>
          )
        :
          null;
        }
      }
    });

答案 1 :(得分:2)

我通常使用微调器来指示小部件正在加载:

resultTags

当state.loading为false时,renderView()执行实际渲染。当state.loading为true时,将显示一个微调器。

在componentWillMount中,我在进行API调用之前将loading设置为true。

render() {
    return this.state.loading ? (
      <div className="spinner">
        <Spinner />
      </div>
    ) : this.renderView();
  }

答案 2 :(得分:1)

更方便(但可以说不太明显)的方法是:

render: function() {
  return (
    this.state.data && 
      <div className="weather-app">
        <MainInfoPanel city={this.state.data.city} today={...} />
        <DayInfos />
        <DayInfos />
        <DayInfos />
        <DayInfos />
      </div>
  );
}

<div>...</div>如果this.state.data真实,将会返回: null;,我将不得不编写一个三元运算符并'12'