Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a problem with one of my React components. I think AJAX doesn't load all the content from external server before React renders the ChildComp component.

Obj tree

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. 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). My guess is that only the first objects are loaded when React renders the component and after that all other inner objects are loaded. 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. How to wait for AJAX response and only after that render the component?

like image 318
Tom Joad Avatar asked Jul 30 '15 11:07

Tom Joad


People also ask

Is there any way to wait for Ajax response and halt execution?

Cut and paste whatever code you need to execute in the callback function passed to success . Some good answer is already provided.

How do you make Ajax call and in which component lifecycle methods should I make an Ajax call?

Where in the component lifecycle should I make an AJAX call? You should populate data with AJAX calls in the componentDidMount lifecycle method. This is so you can use setState to update your component when the data is retrieved.

What is AJAX request and response?

This AJAX Ajax. Response is the object passed as the first argument of all Ajax requests callbacks. This is a wrapper around the native xmlHttpRequest object. It normalizes cross-browser issues while adding support for JSON via the responseJSON and headerJSON properties.

What lifecycle method is the best place to make Ajax calls?

According to official React docs, the recommended place to do Ajax requests is in componentDidMount which is a lifecycle method that runs after the React component has been mounted to the DOM. It sounds a bit counter intuitive to fully render the component before you even start to fetch data.


2 Answers

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>       )    } 
like image 74
Sebastien Lorber Avatar answered Oct 08 '22 17:10

Sebastien Lorber


First of all, you would want to put the AJAX call inside either componentWillMount() or componentDidMount() (if you need to do some DOM manipulation).

componentWillReceiveProps() is

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

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.

So the order of call would be like this (I assumed you put the AJAX call inside 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.

So the solution to your problem is:

1) Moving the AJAX call to either componentWillMount() or componentDidMount()

2) Either set the initial state to just [] (as opposed to [{}]) or check if index is empty in

var listSubscriptions = this.state.response.map(function(index){   return (     index.id   ) }); 
like image 44
Brian Park Avatar answered Oct 08 '22 18:10

Brian Park