Imagine I got an array of promise (12 promise total), I want to render the progress of the promise has resolved on page like: 1/12, 2/12, 3/12 something like that. So I got the idea of how to do it from this answer here: link
I successfully calculated the progressNum or the percentage, and able to console.log them.
The problem is when I try to use setState to set the progressNum, its only show 12/12 when all the promise have resolved. Or render some random num like 4/12 and then 12/12 after, but I want to render something like go from 1/12, 2/12, 3/13 ... 12/12.
Im able to console.log the progress correctly
not render correctly
And I know setState is async, so I try to use react ref to manipulate the element. But didn't get any luck too.
My code so far:
class App extends Component {
state = {
progress: 0,
};
handleResize = async () => {
...
// imgFiles is an array 12 File object
this.allProgress(imgFiles.map(this.resizeImg));
...
};
allProgress(promArray) {
let progress = 0;
promArray.forEach((p) => {
p.then(()=> {
progress += 1;
console.log(`${progress}/12`);
this.setState({ progress });
});
});
return Promise.all(promArray);
}
// I use Jimp package to resize the img, and then return promise
resizeImg = imgFile => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
Jimp.read(reader.result)
.then(async (jimpImg) => {
const normalImg = await this.resizeMain(jimpImg, true,
imgFile.name);
const largeImg = await this.resizeMain(jimpImg, false,
imgFile.name);
resolve([normalImg, largeImg]);
})
.catch(reject);
};
reader.readAsArrayBuffer(imgFile);
});
render() {
return (
<div>
<p>{this.state.progress}</p>
<button onClick={this.handleResize} >Resize</button>
</div> )
}
I also try ref
class App extends Component {
state = {
progress: 0,
};
indicator = React.createRef();
changeProgress = (num) => {
this.indicator.current.innerText = `${num}/12`;
};
...
allProgress(promArray) {
let progress = 0;
promArray.forEach((p) => {
p.then(()=> {
progress += 1;
console.log(`${progress}/12`);
// the only logic that I changed:
this.changeProgress(progress);
});
});
return Promise.all(promArray);
}
...
render() {
return (
<div>
<p ref={this.indicator} />
<button onClick={this.handleResize} >Resize</button>
</div> )
}
}
You can use the callback version of setState
when you want to update the progress to make sure you don't try to update with an old value.
Example
class App extends React.Component {
state = {
done: 0,
total: 12,
data: []
};
componentDidMount() {
Promise.all(
Array.from({ length: this.state.total }, () => {
return new Promise(resolve => {
setTimeout(() => {
this.setState(
prevState => ({ done: prevState.done + 1 }),
() => resolve(Math.random())
);
}, Math.random() * 3000);
});
})
).then(data => {
this.setState({ data });
});
}
render() {
const { done, total, data } = this.state;
return (
<div>
<div>
{done} / {total} done
</div>
<div>{data.join(", ")}</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"><div>
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