【发布时间】:2020-02-11 04:58:39
【问题描述】:
我有一个简单的 React 类,显示 this.state.progress(一个数字),这个状态可以通过 updateProgress(progress) 函数更新。
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
progress: 0,
}
}
updateProgress = (progress) => {this.setState({progress}); };
render() {
let {progress} = this.state;
return <h1>{progress}</h1>;
}
}
我有一个计算密集型函数myHeavyFunc,我需要为此显示进度条。我使用myHeavyFunc 中的循环变量调用上面提到的updateProgress 函数。
myHeavyFunc = async (updateProgress) => {
let loopLength = 1000000;
updateProgress(0);
for(let i=0; i<loopLength; i++) {
// some processing happens here
updateProgress((i+1)/loopLength);
}
}
发生的情况是状态得到更新,我可以通过控制台在setState 回调中记录进度来确认这一点,但组件直到最后才会重新渲染。但是,如果我包含一个 1 毫秒的小 sleep,那么会发生重新渲染,进度更新(显然时间上有巨大的损失,我不喜欢)。
JSFiddle here。在这里,我在单击进度号时运行myHeavyFunc。您可以看到,当 await sleep(1) 被评论时,onClick 在一秒钟内完成,但不显示进度。它甚至不会因任何后续点击而改变。另一方面,如果没有评论,我会得到进度更新,但它只需要永远完成!
我知道 React 出于性能原因应该 batch 更新,但就我而言,在整个循环完成之前我什至看不到一次更新。另外,请注意,我不是在寻找 synchronous setState 函数,但在设置状态后我需要重新渲染(至少仅在进度元素上)。如果它由于批处理而丢失了一些进度更新,我很好,但我确实希望它能够显示进度。
有没有办法在更新 UI 中的进度时以非阻塞方式运行 myHeavyFunc? 在 React 中更新计算密集型函数进度的正确方法是什么?
【问题讨论】:
-
更新fiddle
-
仅仅创建一个函数
async并没有什么神奇的。它让它返回一个 Promise,但是如果它做了一些持续很长时间并且在返回之前是同步的,它仍然会在 React 有机会做任何事情之前运行到完成。 -
而当您添加
await sleep(1)时,这会“暂停执行”并允许组件更新 -
所以简而言之,您使用
await sleep(1)调用的方式是“正确的方式” - 我很欣赏在这种情况下它会使过程太长,在这种情况下只是循环的每 100 步或每 1000 步调用一次。我认为这是基于一个比无操作长循环更现实的示例——在这种情况下,如果你展示你的实际功能,或者至少是一个更现实的近似。 -
@RobinZigmond 我有一个非常相似的设置 - 一个 for 循环,我只是构建一个非常大的数组(~64k json 条目),但是因为这个问题可以推广到我们有一个长时间运行的计算函数,我留下了一个无所事事的循环。
标签: javascript reactjs progress-bar setstate