[英]React - Async calls with an unexpected return order
我有一个react组件(我们称它为Logs
),其中包括另一个react组件(我们称它为DateChanger
),就本例而言,它仅用于更改父组件的日期。 当Logs获得日期更改时,它将进行一次异步调用,以使用服务器中的数据更新其自身的状态:
class Logs extends React.Component {
....
onDateChange(newDate) {
this.setState({loading: true, date: newDate});
asyncCall(newDate)
.then(results => {
this.setState({data: results, loading: false})
});
}
render() {
return (
....
<DateChanger onChange={this.onDateChange}>
....
)
}
}
我遇到的问题是,如果有人快速连续两次更改日期,则呈现的“数据”并不总是来自正确的日期。
因此,我的具体意思是,在此示例中,DateChanger具有一个按钮,可以将日期向前和向后更改1天。 因此,今天是5号,有人可以单击日期更改器上的后退按钮以从4号请求数据,然后再次单击它以从3号请求数据。
asyncCall
, asyncCall
会以错误的顺序返回结果-您单击一次返回并请求第4个,然后再次单击它并请求第3个,然后返回第4个,有时服务器会返回第3个的数据,并且然后第4的数据,因为这是最近它呈现给用户第四届数据.then
进行处理。
无论哪个服务器调用首先返回,React如何确保从第三位而不是第四位呈现数据?
[edit]这与使用setState回调无关。 如果将asyncCall移到setState回调中,则此问题仍然存在。 (尽管如果您想建议我将asyncCall移到setState回调中,我会很乐意接受该建议-但这并不能解决我的问题)
对于一种快速而肮脏的解决方案,您可以检查得到的响应是否是您的组件正在寻找的响应。
onDateChange(newDate) {
this.setState({loading: true, date: newDate});
asyncCall(newDate)
.then(results => {
// Update state only if we are still on the date this request is for.
if (this.state.date === newDate) {
this.setState({data: results, loading: false})
}
});
}
但是,这样的解决方案具有更好的关注点分离和更高的可重用性。
// Request manager constructor.
// Enables you to have many different contexts for concurrent requests.
const createRequestManager = () => {
let currentRequest;
// Request manager.
// Makes sure only last created concurrent request succeeds.
return promise => {
if (currentRequest) {
currentRequest.abort();
}
let aborted = false;
const customPromise = promise.then(result => {
if (aborted) {
throw new Error('Promise aborted.');
}
currentRequest = null;
return result;
});
customPromise.abort = () => {
aborted = true;
};
return customPromise;
};
};
class Logs extends React.Component {
constructor(props) {
super(props);
// Create request manager and name it “attemptRequest”.
this.attemptRequest = createRequestManager();
}
onDateChange(newDate) {
this.setState({loading: true, date: newDate});
// Wrap our promise using request manager.
this.attemptRequest(asyncCall(newDate))
.then(results => {
this.setState({data: results, loading: false})
});
}
render() {
return (
....
<DateChanger onChange={this.onDateChange}>
....
)
}
}
请记住,我们实际上并没有中止请求,只是忽略了它的结果。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.