简体   繁体   中英

Why loading state is not being updated when using async function in onClick?

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>
    </>
  );
};

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.

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.

As @NiceBooks said localhost would be quite fast to notice the loading, I used setTimeout , so the useState can be reflected in onClick ():

        onClick={() => {
           setIsDownloadLoading(true);
           setTimeout(() => {
              downloadFile(record.supplierInstrument.id, record.name);
          }, 3000);
        }}

Now I am able to see the loading indicator.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM