简体   繁体   English

如何等待AJAX​​响应并且只有在渲染组件之后?

[英]How to wait for AJAX response and only after that render the component?

I have a problem with one of my React components. 我的一个React组件有问题。 I think AJAX doesn't load all the content from external server before React renders the ChildComp component. 我认为在React呈现ChildComp组件之前,AJAX不会从外部服务器加载所有内容。

Obj树

Above you can see the tree of response which is coming from server. 在上面你可以看到来自服务器的响应树。 And this is my component's code: 这是我的组件代码:

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

This is working just fine, but if I change my return to: 这工作正常,但如果我改变我的回报:

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

id is undefined. id未定义。 And all of the other properties of order object. 以及订单对象的所有其他属性。 Why is it like this? 为什么会这样? If I console.log my response after success function it gives all the data (like in picture). 如果我在success函数后console.log我的响应,它会提供所有数据(如图片所示)。 My guess is that only the first objects are loaded when React renders the component and after that all other inner objects are loaded. 我的猜测是,当React渲染组件时,只加载第一个对象,然后加载所有其他内部对象。 I can't really say if that's the case (sounds so weird) nor I can't say how to solve this out. 我真的不能说是不是这样(听起来很奇怪),也不能说如何解决这个问题。

I tried also something like this 我也尝试过类似的东西

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

but still re-render happens before my console.log(data) is triggered. 但仍然在我的console.log(data)被触发之前重新渲染。 How to wait for AJAX response and only after that render the component? 如何等待AJAX​​响应并且只有在渲染组件之后?

Here's your code reworked with some comments of the modified parts 这是您的代码重做了一些修改部分的注释

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

First of all, you would want to put the AJAX call inside either componentWillMount() or componentDidMount() (if you need to do some DOM manipulation). 首先,您需要将AJAX调用放在componentWillMount()componentDidMount() (如果您需要进行一些DOM操作)。

componentWillReceiveProps() is componentWillReceiveProps()

Invoked when a component is receiving new props. 组件接收新道具时调用。 This method is not called for the initial render . 初始渲染不会调用此方法

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

But even if you put the AJAX call inside componentWillMount() or componentDidMount() , render() will probably be called with an empty response before AJAX call finishes. 但即使你将AJAX调用放在componentWillMount()componentDidMount() ,在AJAX调用完成之前,可能会使用空响应调用render()

So the order of call would be like this (I assumed you put the AJAX call inside componentWillMount() : 所以调用的顺序就是这样(我假设你把AJAX调用放在componentWillMount()

componentWillMount() (AJAX call is triggered) -> componentDidMount() -> render() (render with empty response; so index.order.id will cause an error) -> AJAX call returns and calls this.setState() -> (some other component lifecycle methods such as shouldComponentUpdate() is called; see the FB link above for details) -> render() is called again. componentWillMount() (触发AJAX调用) - > componentDidMount() - > render() (渲染空响应;所以index.order.id会导致错误) - > AJAX调用返回并调用this.setState() - > (调用一些其他组件生命周期方法,例如shouldComponentUpdate() ;有关详细信息,请参阅上面的FB链接) - >再次调用render()

So the solution to your problem is: 所以你的问题的解决方案是:

1) Moving the AJAX call to either componentWillMount() or componentDidMount() 1)将AJAX调用移动到componentWillMount()componentDidMount()

2) Either set the initial state to just [] (as opposed to [{}] ) or check if index is empty in 2)将初始状态设置为只有[] (而不是[{}] )或检查index是否为空

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

I threw it into a JSBin and I can't seem to reproduce your problem. 我把它扔进了一个JSBin,我似乎无法重现你的问题。

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

Every time the button is clicked, it sends new props to <ChildComp/> , which then triggers its componentWillReceiveProps , sends the (fake) ajax request, receives the (fake) response, sets the response state to the (fake) response, and re-renders the component with the correct data shown. 每次单击该按钮时,它会向<ChildComp/>发送新的道具,然后触发其componentWillReceiveProps ,发送(假)ajax请求,接收(假)响应,将响应状态设置为(假)响应,以及使用显示的正确数据重新呈现组件。

The only thing I added was a check for index.id == null before trying to access it, but I don't think that does anything to solve the issue you're having about being unable to access index.order.id . 我添加的唯一内容是在尝试访问它之前检查index.id == null ,但我认为没有任何方法可以解决您无法访问index.order.id

The only reason I can come up with as to why you can't access index.order.id is because perhaps you have some code that you aren't showing us that modifies the response at some point. 我能想出为什么你不能访问index.order.id的唯一原因是因为你可能有一些你没有向我们展示的代码在某些时候修改了响应。

Also, remember that React components will always re-render when its state or props have changed, unless you have defined behavior in shouldComponentUpdate that says otherwise. 另外,请记住React组件在其状态或props更改时将始终重新呈现,除非您在shouldComponentUpdate中定义了shouldComponentUpdate说明的行为。 This means that when you receive the whole object from the server and set it to a state in your component, it will re-render and show you the updated state. 这意味着当您从服务器接收整个对象并将其设置为组件中的状态时,它将重新呈现并显示更新的状态。 There is no reason/way for React to "shallowly render" your response object like you were suggesting in your original post. React没有理由/方式像您在原始帖子中建议的那样“浅度渲染”您的响应对象。

I'm not sure if this answer helps, but hopefully you can look at the JSBin I provided and find something that you might have overlooked. 我不确定这个答案是否有帮助,但希望你能看一下我提供的JSBin并找到你可能忽略的东西。

Just do the AJAX call in componentDidMount() and in render you can simply check... 只需在componentDidMount()进行AJAX调用,然后在渲染中只需检查...

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

// do your stuff here

}

OR if you are using react 16.6 or more, you can simply use lazy components. 或者,如果您使用的是16.6或更高版本,则可以使用惰性组件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM