使用 setState 从计算密集型函数中更新进度

Sar*_*ran 6 javascript setstate progress-bar reactjs

我有一个简单的 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>;
  }
}
Run Code Online (Sandbox Code Playgroud)

我有一个计算密集型函数myHeavyFunc,我需要显示进度条。我updateProgress使用内部的循环变量调用上面提到的函数myHeavyFunc

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>;
  }
}
Run Code Online (Sandbox Code Playgroud)

发生的情况是状态被更新,我可以通过控制台记录setState回调中的进度来确认这一点,但组件直到最后才会重新渲染。但是,如果我包含 1毫秒的小睡眠,则重新渲染发生,进度更新(显然时间损失很大,我不喜欢)。

JSFiddle在这里。在这里,我myHeavyFunc点击进度号运行。你可以看到 whenawait sleep(1)被评论,onClick在一秒钟内完成,但不显示进度。它甚至不会因任何后续点击而改变。另一方面,如果没有评论,我会收到进度更新,但需要很长时间才能完成!

我知道 React 会出于性能原因批量更新,但就我而言,在整个循环完成之前,我什至看不到一个更新发生。另外,请注意,我不是在寻找同步 setState函数,而是在设置状态后需要重新渲染(至少仅在进度元素上)。如果由于批处理而丢失一些进度更新,我很好,但我确实希望它显示进度。

有没有办法myHeavyFunc在更新 UI 进度的同时以非阻塞方式运行?在 React 中更新计算密集型函数的进度的正确方法是什么?

Bes*_*lia 0

我猜问题出在浏览器重画上。通常,浏览器每秒重绘 60 帧。在您的情况下,您要求浏览器执行超出其能力的绘画。我建议使用requestAnimationFrame而不是 for 循环,以不断更新 DOM。requestAnimationFrame将处理浏览器执行下一次重绘之前要调用的函数。

  let i = 0
  let loopLength = 1000;

  let myHeavyFunc = (updateProgress) => {
    updateProgress((i+1)/loopLength);
    i++;
  if (i < loopLength) {
    window.requestAnimationFrame(() => myHeavyFunc(updateProgress));
    
  }
}
Run Code Online (Sandbox Code Playgroud)

工作演示https://jsfiddle.net/gdq5ouat/5/