Javascript 如何等待 AJAX 响应,然后才渲染组件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31723095/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to wait for AJAX response and only after that render the component?
提问by Tom Joad
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.
我的 React 组件之一有问题。我认为 AJAX 在 React 呈现ChildComp
组件之前不会从外部服务器加载所有内容。
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.
id 未定义。以及订单对象的所有其他属性。为什么会这样?如果我console.log
在success
函数后做出响应,它会提供所有数据(如图所示)。我的猜测是当 React 渲染组件时只加载第一个对象,然后加载所有其他内部对象。我真的不能说是不是这种情况(听起来很奇怪),我也不能说如何解决这个问题。
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?
但在console.log(data)
触发我之前仍然会重新渲染。如何等待 AJAX 响应,然后才渲染组件?
采纳答案by Sebastien Lorber
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>
)
}
回答by Brian Park
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()
or 中componentDidMount()
(如果您需要进行一些 DOM 操作)。
componentWillReceiveProps()
is
componentWillReceiveProps()
是
Invoked when a component is receiving new props. This method is not called for the initial render.
当组件接收新的 props 时调用。不会为初始渲染调用此方法。
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()
or 中componentDidMount()
,render()
也可能会在 AJAX 调用完成之前以空响应调用。
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) 要么将初始状态设置为 just []
(而不是[{}]
),要么检查中是否index
为空
var listSubscriptions = this.state.response.map(function(index){
return (
index.id
)
});
回答by Michael Parker
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. 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 组件在其 state 或 props 发生变化时总是会重新渲染,除非您在其中定义了行为shouldComponentUpdate
。这意味着当您从服务器接收整个对象并将其设置为组件中的状态时,它将重新呈现并向您显示更新的状态。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 并找到您可能忽略的内容。
回答by Diptanshu Pandey
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.
或者,如果您使用的是 react 16.6 或更高版本,则可以简单地使用惰性组件。