I have a simple React class showing this.state.progress (a number) and this state can be updated via updateProgress(progress) function.
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>;
  }
}
I have a compute intensive function myHeavyFunc, for which I need to show the progress bar. I call updateProgress function I mentioned above using the loop variable inside myHeavyFunc.
myHeavyFunc = async (updateProgress) => {
    let loopLength = 1000000;
    updateProgress(0);
    for(let i=0; i<loopLength; i++) {
      // some processing happens here
      updateProgress((i+1)/loopLength);
    }
}
What happens is that state gets updated, and I can confirm that by console logging progress in the setState callback, but the component doesn't re-render until the very end. However, if I include a small sleep of 1ms, then the re-render happens, progress updates (obviously with a huge loss in time, which I do not prefer). 
JSFiddle here. Here I run myHeavyFunc on clicking the progress number. You can see that when await sleep(1) is commented, onClick finishes in a second, but does NOT show progress. It does not even change for any subsequent clicks. On the other hand,  if it is not commented, I get the progress updates, but it just takes forever to complete!
I am aware that React shall batch the updates for performance reasons, but in my case, I can't even see one update happen till the whole loop finishes. Also, please note that I am NOT looking for a synchronous setState function, but I need re-rendering (atleast on the progress element alone) after the state is set. I am fine if it drops a few progress updates due to batching, but I do expect it to show progress.  
Is there a way to run myHeavyFunc in a non-blocking manner while updating the progress in the UI? What is the right way to update progress of compute intensive functions in React?
I recommend you notify the progress as few times as possible. The progress will go from 0 to 100, so I will notify the progress only 100 times, and also I will add a timeout, so the user can see the progress transition:
If loopLength = 1000000,
1000000 / 100 = 10000
Every 10000 iterations I will call updateProgress function.
let myHeavyFunc = async (updateProgress) => {
  let loopLength = 1000000;
  let current = 0;
  for(let i=0; i<loopLength; i++) {
    const result = Math.floor(((i+1)/loopLength)*100);
    if(current != result) {
       setTimeout(()=> {
           updateProgress(result);
       }, result *100);
    }
    current = result;
  }
}
See: https://jsfiddle.net/wbzx4j90/1/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With