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