React: Load Spinners and File Download
I had created a bootstrap table that displays a list of items with a download button for each row. The download button calls an API that uses application/octet-stream
to return a zip file. The download button’s handler looks like this:
...
return fetch(`${reqUrl}`, {
headers: new Headers({
"Accept": "application/octet-stream"
})
})
.then((response) => {
return response.blob()
})
.then(blob => {
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = `${fileName}`;
document.body.appendChild(a);
a.click();
a.remove();
});
...
Some of the files are over a 100MBs so there is sometimes a very long delay between clicking the button and seeing the download. So adding a load spinner would enhance the user experience.
One solution I found (Download API Files With React & Fetch) was to use a state variable:
...
this.state = {
inProgress: false,
};
...
The download hander can then use that variable:
this.setState({
inProgress: true
}, () => {
return fetch(`${reqUrl}`, {
headers: new Headers({
"Accept": "application/octet-stream"
})
})
.then((response) => {
return response.blob()
})
.then(blob => {
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = `${fileName}`;
document.body.appendChild(a);
a.click();
a.remove();
this.setState({ inProgress: false })
});
});
The table div can use this same variable to change the opacity of the table and to show the loader:
return (
<div style={{ opacity: this.state.inProgress ? '.5' : '1' }}>
<BootstrapTable>
...
</BootstrapTable>
{this.state.inProgress ? (
<>
<div className="loading-overlay">
<i className="loading-icon"></i>
<div>Loading...</div>
</div>
</>
) : null}
</div>
);
That’s it!