我正在构建一个应用程序,其中用户提供文件和一些参数以执行长时间运行的任务。我有所有的工作。无法正常工作的是向用户显示当前的处理进度。我设置了a simple CodePen来说明。
在Pen中,我有一个按钮可以在while循环中运行任务。如果我正在看控制台,那么在逐步浏览过程中,可以看到打印的进度。但是,状态直到循环完成才更新,因此在UI中,进度从0跳到5而不显示中间值。在这里,我使用睡眠功能模拟任务,实际上我没有在应用程序中使用睡眠功能。
我已经做过一些研究,我知道这与setState异步以及React批处理更新一起使用,以提高呈现UI的效率有关。
话虽如此,我想知道向用户显示进度的最佳方法是什么。使用React的状态是行不通的,我已经尝试直接写入DOM了,但是那是行不通的(而且这样做似乎不是一种干净的方法)。我是否需要使用一些其他库来执行此操作,或者我缺少什么?我当时正在考虑将其移到一个单独的过程中,然后将进度传达回应用程序,但是我是否会遇到未更新UI的同一问题?
也可能很重要,我正在使用while循环,因为我正在使用生成器,所以我知道我不会收到太多进度更新,因为yield将每个百分比从0扩展到100。我也很容易如果更好,请删除生成器/产量部分。
我的代码在CodePen中以及以下版本中:
--- HTML ---
<div id="app"></app>
--- JSX ---
class Application extends React.Component {
constructor(props) {
super(props);
this.state = {
progress: 0
};
this.doTask = this.doTask.bind(this);
this.sleep = this.sleep.bind(this);
}
sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
doTask() {
let count = 0;
while(count<5) {
count++;
console.log(count);
this.setState({
progress: count
});
this.sleep(500);
}
}
render() {
return <div>
<button onClick={this.doTask}>Do Task</button>
<div>Progress: {this.state.progress}</div>
</div>;
}
}
/*
* Render the above component into the div#app
*/
React.render(<Application />, document.getElementById('app'));
答案 0 :(得分:0)
这是开发中经常发生的问题,所以我希望这个问题和解决方案可以帮助您:Calling setState in a loop only updates state 1 time。
我还将看一下https://jsbin.com/kiyaco/edit?js,output,其中使用了this.setState的替代形式。它本质上传入了可以访问当前状态的函数。
答案 1 :(得分:0)
我最终要做的是创建一个基本上充当服务器的后台进程(又名隐藏窗口,因为我正在使用电子)。我长时间运行的进程的信息通过websocket发送到后台进程,进度信息也通过websocket发送回我的主要组件。在我看来,从循环更新状态看起来更直观,但是在后台运行代码不会冻结UI,现在我可以实现所需的行为。
答案 2 :(得分:0)
我有同样的问题,并使用了解决方法。由于某些原因,在使用setTimeout()
时,页面会正确进行setState()
处理。它使我想起了JavaFX中的runLater
。
这是我使用的解决方法:
// a wrapper function for setState which uses `Promise` and calls `setTimeout()`
setStatePromise = (state) => new Promise(resolve => {
this.setState(state, () => {
setTimeout(() => resolve(), 1);
});
});
现在您可以改为调用此包装器。但是您需要使用异步功能:
async doTask() {
let count = 0;
while(count<5) {
count++;
console.log(count);
await this.setStatePromise({
progress: count
});
this.sleep(500);
}
}