Как ждать ответа AJAX и только после этого сделать компонент?
У меня проблема с одной из моих компонентов React. Я думаю, что AJAX не загружает весь контент с внешнего сервера, прежде чем React отобразит компонент ChildComp
.
![Obj tree]()
Выше вы можете увидеть дерево ответа, которое поступает с сервера. И это мой код компонента:
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 undefined. И все остальные свойства объекта заказа. Почему так? Если я console.log
мой ответ после функции success
, он дает все данные (например, на картинке). Я предполагаю, что только первые объекты загружаются, когда React отображает компонент, и после этого загружаются все остальные внутренние объекты. Я не могу сказать, если это случай (звучит так странно), и я не могу сказать, как это решить.
Я тоже попробовал что-то вроде этого
success: function(data){
self.setState({
response: data
}, function(){
self.forceUpdate();
})
}.bind(this)
но повторная визуализация выполняется до того, как будет запущен мой console.log(data)
. Как ждать ответа AJAX и только после этого визуализировать компонент?
Ответы
Ответ 1
Здесь ваш код переработан с некоторыми комментариями измененных частей
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>
)
}
Ответ 2
Прежде всего, вы хотите поместить вызов AJAX внутри componentWillMount()
или componentDidMount()
(если вам нужно сделать некоторые манипуляции с DOM).
componentWillReceiveProps()
является
Вызывается, когда компонент получает новые реквизиты. Этот метод не вызывается для исходного рендеринга.
https://facebook.github.io/react/docs/component-specs.html
Но даже если вы поместите вызов AJAX внутри componentWillMount()
или componentDidMount()
, render()
, вероятно, будет вызван с пустым ответом до завершения вызова AJAX.
Таким образом, порядок вызова будет таким (я предположил, что вы поместили вызов AJAX внутри componentWillMount()
:
componentWillMount()
(вызов AJAX запускается) → componentDidMount()
→ render()
(рендеринг с пустым ответом, поэтому index.order.id
приведет к ошибке) → Возврат AJAX и вызовы this.setState()
→ ( вызывается несколько других методов жизненного цикла компонента, таких как shouldComponentUpdate()
, см. ссылку FB выше для деталей) → render()
вызывается снова.
Итак, решение вашей проблемы:
1) Перемещение вызова AJAX на componentWillMount()
или componentDidMount()
2) Либо установите начальное состояние на []
(в отличие от [{}]
), либо проверьте, что index
пуст в
var listSubscriptions = this.state.response.map(function(index){
return (
index.id
)
});
Ответ 3
Я бросил его в JSBin, и я не могу воспроизвести вашу проблему.
http://jsbin.com/jefufe/edit?js,output
При каждом нажатии кнопки он отправляет новые реквизиты в <ChildComp/>
, который затем запускает свой componentWillReceiveProps
, отправляет (фальшивый) запрос ajax, получает (фальшивый) ответ, устанавливает состояние ответа на (поддельный ) и повторно отображает компонент с указанными правильными данными.
Единственное, что я добавил, это проверка на index.id == null
, прежде чем пытаться получить к нему доступ, но я не думаю, что это что-то для решения проблемы, с которой вы сталкиваетесь, о невозможности доступа к index.order.id
.
Единственная причина, по которой я могу понять, почему вы не можете получить доступ к index.order.id
, состоит в том, что, возможно, у вас есть код, который вы не показываете нам, который в какой-то момент меняет ответ.
Кроме того, помните, что компоненты React всегда будут повторно отображаться, когда изменилось его состояние или реквизит, если вы не определили поведение в shouldComponentUpdate
, которое говорит иначе. Это означает, что, когда вы получаете весь объект с сервера и устанавливаете его в состояние в своем компоненте, он будет повторно отображать и показывать вам обновленное состояние. Для React нет причины/способа "мелко визуализировать" ваш объект ответа, как вы предлагали в своем исходном сообщении.
Я не уверен, помогает ли этот ответ, но, надеюсь, вы можете посмотреть JSBin, который я предоставил, и найти то, что вы могли упустить.
Ответ 4
Просто выполните AJAX-вызов в componentDidMount()
и в рендере вы можете просто проверить...
if(this.state.response === '' || this.state.response === [] || this.state.response === undefined)
return <Loader />
else{
// do your stuff here
}
ИЛИ если вы используете реакцию 16.6 или более, вы можете просто использовать ленивые компоненты.