I am trying to use MUI Loading Button and change the loading state of the button when it is clicked.
The problem is that I am always getting false for downloadLoading
even that I set its state to true
in onClick
event.
I have async function (downloadFile
) in onClick
, but I am setting the state of the loading before the function. When I log the state of downloadLoading
inside the onClick
and async function, it is false
. When I remove the downloadFile
function from onClick
, state is set to true
and I am able to see the loading indicator. How can I solve this problem? Why is it happening?
export const Row: React.FC<IProps> = ({
record,
}: IProps): JSX.Element => {
const [downloadLoading, setDownloadLoading] = useState<boolean>(false);
useEffect(() => {
return () => {
setDownloadLoading(false);
};
}, []);
const download = (content: any, fileName: string) => {
const link = document.createElement("a");
link.setAttribute("href", URL.createObjectURL(content));
link.setAttribute("download", `${fileName}.pdf`);
link.style.visibility = "hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
const downloadFile = async (
e: React.MouseEvent<HTMLElement>,
id: string | undefined,
fileName: string
) => {
e.preventDefault();
try {
const response = await fetch(
`http://localhost:8080/download/id/`
id
);
if (!response.ok) {
response.json().then((value) => {
console.log("val",value)
});
} else {
const content = await response.blob();
download(content, fileName);
}
setDownloadLoading(false);
} catch (e) {
console.log("error",e)
}
};
return (
<>
<TableRow>
<TableCell align="left" size="medium">
{record.name}
</TableCell>
<TableCell size="small">
<OutlinedCustomButton
loading={downloadLoading}
loadingIndicator={<CircularProgress color="inherit" size={20} />}
loadingPosition="end"
endIcon={<FileDownloadIcon />}
fullWidth
onClick={(e) => {
setDownloadLoading(true);
console.log(downloadLoading);
downloadFile(e, record.id, record.name);
}}
>
Download
</OutlinedCustomButton>
</TableCell>
</TableRow>
</>
);
};
CodePudding user response:
You are getting false
for downloadLoading
probably because of this part. At each re-render, it's setting the var to false:
useEffect(() => {
return () => {
setDownloadLoading(false);
};
}, []);
If you are already initializing the var as false, no need for that useEffect:
const [downloadLoading, setDownloadLoading] = useState<boolean>(false);
Also, it's worth mentioning a good practice here: if your var holds the value for a state like "process is loading" or "process is not loading", a more intuitive name would be "isLoading", instead of "downloadLoading". That way, you can see right away this var holds a boolean value.
CodePudding user response:
Your 1st useEffect
is unnecessary as it only contains a cleanup function (equivalent to componentWillUnmount
) that resets the state at the end.
Try converting the component to a class based one:
In the onClick
callback:
onClick={(e)=> {
this.setState({downloadLoading: true}, ()=> {
// download happens after downloadLoading is set to true
downloadFile(e, record.id, record.name);
})
}}
To test, download a really large file or host the server, as localhost
would be quite fast, to notice the loading.