How to wait for AJAX response and only after that render the component?
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
)
});
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>
)
}