我有一个反应组件(让我们称之为Logs
),其中包含另一个反应组件(让我们称之为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
以错误的顺序返回结果 - 您单击一次并请求第4个,然后再次单击它并在返回第4个之前请求第3个 然后,服务器返回第3个数据,然后返回第4个数据,然后向用户呈现第4个数据,因为这是最近处理的.then
。
确保从第3个而不是第4个呈现数据的React方法是什么,无论哪个服务器调用首先返回?
[edit]这不是关于使用setState回调。如果我在setState回调中移动了我的asyncCall,这个问题仍然存在。 (虽然如果你想建议我应该在setState回调中移动我的asyncCall,我很乐意接受这个建议 - 它只是没有解决我的问题)
答案 0 :(得分:1)
对于快速而肮脏的解决方案,您可以检查您获得的响应是否是您的组件正在寻找的响应。
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}>
....
)
}
}
请记住,我们实际上并没有中止请求,只是忽略了它的结果。