Javascript 如何等待AJAX响应并仅在响应之后呈现组件?

Javascript 如何等待AJAX响应并仅在响应之后呈现组件?,javascript,ajax,reactjs,Javascript,Ajax,Reactjs,我的一个组件有问题。我认为在React呈现ChildComp组件之前,AJAX不会从外部服务器加载所有内容 在上面,您可以看到来自服务器的响应树。这是我的组件代码: var ChildComp = React.createClass({ getInitialState: function(){ return { response: [{}] } }, componentWillReceiveProps: function(nextProps){

我的一个组件有问题。我认为在React呈现
ChildComp
组件之前,AJAX不会从外部服务器加载所有内容

在上面,您可以看到来自服务器的响应树。这是我的组件代码:

var ChildComp = React.createClass({
  getInitialState: function(){
    return {
      response: [{}]
    }
  },
  componentWillReceiveProps: function(nextProps){
    var self = this;

    $.ajax({
      type: 'GET',
      url: '/subscription',
      data: {
        subscriptionId: nextProps.subscriptionId //1
      },
      success: function(data){
        self.setState({
          response: data
        });
        console.log(data);
      }
    });
  },
  render: function(){
      var listSubscriptions = this.state.response.map(function(index){
        return (
          index.id
        )
      });
      return (
        <div>
          {listSubscriptions}
        </div>
      ) 
  }
});
id未定义。以及order对象的所有其他属性。为什么是这样?如果我
console.log
我在
success
函数之后的响应,它会给出所有数据(如图所示)。我的猜测是,当React渲染组件时,只加载第一个对象,然后加载所有其他内部对象。我真的不知道是不是这样(听起来很奇怪),也不知道如何解决这个问题

我也试过类似的东西

  success: function(data){
    self.setState({
      response: data
    }, function(){
      self.forceUpdate();
    })
  }.bind(this)

但仍然会在触发my
console.log(数据)
之前进行重新渲染。如何等待AJAX响应并仅在该响应之后呈现组件?

首先,您需要将AJAX调用放入
componentWillMount()
componentDidMount()
(如果需要执行一些DOM操作)

componentWillReceiveProps()
is

当组件接收新道具时调用初始渲染时不调用此方法

但是,即使将AJAX调用放在
componentWillMount()
componentDidMount()
中,在AJAX调用完成之前,
render()
也可能会被调用为空响应

所以调用的顺序是这样的(我假设您将AJAX调用放在
componentWillMount()
中:

componentWillMount()
(AJAX调用被触发)->
componentDidMount()
->
render()
(以空响应呈现;因此
index.order.id
将导致错误)->AJAX调用返回并调用
this.setState()
->(其他一些组件生命周期方法,如
shouldComponentUpdate()
被调用;有关详细信息,请参见上面的FB链接)->
render()
再次被调用

因此,您的问题的解决方案是:

1) 将AJAX调用移动到
componentWillMount()
componentDidMount()

2) 要么将初始状态设置为刚好
[]
(与
[{}]
相反),要么检查
索引中是否为空

var listSubscriptions = this.state.response.map(function(index){
  return (
    index.id
  )
});

下面是您的代码,其中包含一些修改部分的注释

  getInitialState: function(){
    return {
      // [{}] is weird, either use undefined (or [] but undefined is better).
      // If you use [], you loose the information of a "pending" request, as 
      // you won't be able to make a distinction between a pending request, 
      // and a response that returns an empty array
      response: undefined
    }
  },



render:function(){
//处理响应尚未出现的情况
如果(!this.state.response){
//请注意,如果您不想在dom中放入任何内容,则可以返回false
//这也是您渲染微调器或其他东西的机会。。。
还没到这里就把它还给我!
}
//让您有机会处理ajax请求
//已完成,但结果数组为空
if(this.state.response.length==0){
返回未找到此订阅的结果;
} 
//正常情况
var listSubscriptions=this.state.response.map(函数(索引){
返回(
索引id
)
});
返回(
{listSubscriptions}
) 
}

我把它扔进了JSBin,似乎无法重现您的问题

每次单击按钮时,它都会将新的道具发送到
,然后触发其
组件WillReceiveProps
,发送(假)ajax请求,接收(假)响应,将响应状态设置为(假)响应,并使用显示的正确数据重新呈现组件

我添加的唯一内容是在尝试访问它之前检查
index.id==null
,但我认为这并不能解决您无法访问
index.order.id
的问题

关于您无法访问
index.order.id
的唯一原因是,您可能有一些代码没有显示给我们,它们在某个时候修改了响应

此外,请记住,当React组件的状态或道具发生更改时,它将始终重新渲染,除非您在
shouldComponentUpdate
中定义了另一种行为。这意味着,当您从服务器接收整个对象并将其设置为组件中的状态时,它将重新渲染并向您显示更新的状态。没有理由/方式像您在原始帖子中建议的那样“肤浅地呈现”您的响应对象


我不确定这个答案是否有帮助,但希望您可以查看我提供的JSBin,找到一些您可能忽略的内容。

只需在
componentDidMount()
中执行AJAX调用,在render中您只需检查一下

if(this.state.response === '' || this.state.response === [] || this.state.response === undefined)
return <Loader />
else{

// do your stuff here

}
if(this.state.response==''this.state.response===[]| | this.state.response===未定义)
返回
否则{
//在这里做你的事
}

或者,如果您使用的是react 16.6或更高版本,您可以简单地使用惰性组件。

OP在其ajax请求数据中使用收到的道具。这个答案对他不起作用。将Ajax移动到
will
did
mount不会解决问题;在Ajax调用完成之前,几乎肯定还会有一个呈现。为了确保响应数据的格式符合您的要求,您应该向Ajax请求中添加
dataType:“json”
。此外,您还可以尝试在
shouldComponentUpdate
中记录接收到的新状态。最后,验证
index.order
上有哪些属性(如果有)。可能它包含一个字符串而不是属性。我想指出,使用React 16,componentLifecycle已更改,因此其中一些答案可能已过时。我甚至不确定这里的解决代码是什么,但我猜是
componentWillMount
函数。AJAX现在是
  loadSubscriptionData: function(subscriptionId){
    var self = this;

    // You probably want to redo the ajax request only when the 
    // subscriptionId has changed (I guess?)
    var subscriptionIdHasChanged = 
       (this.props.subscriptionId !== subscriptionId)

    if ( subscriptionIdHasChanged ) {
        // Not required, but can be useful if you want to provide the user
        // immediate feedback and erase the existing content before 
        // the new response has been loaded
        this.setState({response: undefined});

        $.ajax({
          type: 'GET',
          url: '/subscription',
          data: {
            subscriptionId: subscriptionId //1
          },
          success: function(data){

            // Prevent concurrency issues if subscriptionId changes often: 
            // you are only interested in the results of the last ajax    
            // request that was made.
            if ( self.props.subscriptionId === subscriptionId ) {
                self.setState({
                  response: data
                });
            }
          }
        });
     }
  },
  // You want to load subscriptions not only when the component update but also when it gets mounted. 
  componentDidMount: function(){
    this.loadSubscriptionData(this.props.subscriptionId);
  },
  componentWillReceiveProps: function(nextProps){
    this.loadSubscriptionData(nextProps.subscriptionId);
  },
  render: function(){

      // Handle case where the response is not here yet
      if ( !this.state.response ) {
         // Note that you can return false it you want nothing to be put in the dom
         // This is also your chance to render a spinner or something...
         return <div>The responsive it not here yet!</div>
      }

      // Gives you the opportunity to handle the case where the ajax request
      // completed but the result array is empty
      if ( this.state.response.length === 0 ) {
          return <div>No result found for this subscription</div>;
      } 


      // Normal case         
      var listSubscriptions = this.state.response.map(function(index){
        return (
          index.id
        )
      });
      return (
        <div>
          {listSubscriptions}
        </div>
      ) 
  }
if(this.state.response === '' || this.state.response === [] || this.state.response === undefined)
return <Loader />
else{

// do your stuff here

}