如何等待 AJAX 响应,然后才渲染组件?

IT技术 javascript ajax reactjs
2021-04-17 11:41:19

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

对象树

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

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

这工作得很好,但如果我将返回更改为:

  return (
     index.id + " " + index.order.id
  )

id 未定义。以及订单对象的所有其他属性。为什么会这样?如果我console.logsuccess函数后做出响应,它会提供所有数据(如图所示)。我的猜测是当 React 渲染组件时只加载第一个对象,然后加载所有其他内部对象。我真的不能说是不是这种情况(听起来很奇怪),我也不能说如何解决这个问题。

我也尝试过这样的事情

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

但在console.log(data)触发我之前仍然会重新渲染如何等待 AJAX 响应,然后才渲染组件?

4个回答

这是您的代码修改后的部分注释

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

  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>
      ) 
  }
将 state 属性设置为 undefined 给我带来了错误。但是感谢提示我最终使用了标志属性loaded: false
2021-05-24 11:41:19
甚至不确定这里的求解代码是什么,但我猜是那个componentWillMount函数。AJAX 现在正在从服务器加载所有内容。tbh 对我来说没有多大意义,但我会更深入地研究它,尽管现在一切正常。
2021-06-01 11:41:19
@TomJoadcomponentWillReceiveProps是,如文档所述,不会在初始渲染时调用,因此也使用componentWillMount
2021-06-14 11:41:19
@MarcelDjaman 这不是错误,这是预期的:如果您使用undefined, thenthis.state是未定义的,并且您不能this.state.loaded
2021-06-18 11:41:19
只是作为后续; React 文档声明componentDidMount出于各种原因执行 Ajax,并且执行 Ajax incomponentWillMount会导致各种问题(例如,如果执行服务器端渲染)。
2021-06-18 11:41:19

首先,您可能希望将 AJAX 调用放在componentWillMount()or 中componentDidMount()(如果您需要进行一些 DOM 操作)。

componentWillReceiveProps()

当组件接收新的 props 时调用。初始渲染不会调用此方法

https://facebook.github.io/react/docs/component-specs.html

但即使您将 AJAX 调用放入componentWillMount()or 中componentDidMount()render()也可能会在 AJAX 调用完成之前以空响应调用。

所以调用顺序是这样的(我假设你把 AJAX 调用放在里面componentWillMount()

componentWillMount()(触发AJAX调用)-> componentDidMount()-> render()(渲染为空响应;所以index.order.id会导致错误)->AJAX调用返回和调用this.setState()->(其他一些组件生命周期方法如shouldComponentUpdate()被调用;详见上面的FB链接) ->render()再次被调用。

所以你的问题的解决方案是:

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

2) 要么将初始状态设置为 just [](而不是[{}]),要么检查中是否index为空

var listSubscriptions = this.state.response.map(function(index){
  return (
    index.id
  )
});
将 Ajax 移动到任何一个willdidmount 都不能解决问题;在 Ajax 调用完成之前,几乎肯定还会有一次渲染。
2021-05-31 11:41:19
OP 在他的 ajax 请求数据中使用收到的道具。这个答案对他不起作用。
2021-06-15 11:41:19

我把它扔进了一个 JSBin,我似乎无法重现你的问题。

http://jsbin.com/jefufe/edit?js,output

每次单击按钮时,它都会向 发送新props<ChildComp/>,然后触发其componentWillReceiveProps,发送(假)ajax 请求,接收(假)响应,将响应状态设置为(假)响应,并重新渲染组件显示正确的数据。

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

我能想出您为什么无法访问的唯一原因index.order.id是因为您可能有一些代码没有向我们展示,这些代码在某些时候会修改响应。

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

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

只需componentDidMount()在渲染中进行 AJAX 调用,您就可以简单地检查...

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

// do your stuff here

}

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