javascriptreactjsgetbloboctet-stream

Downloading file onClick is downloading on refresh


In my documents table I'm mapping through some metadata to get a filename and docid that is getting passed to my DocumentDownloadButton:

const DocumentsTableBody = ({ documentMetadata, tableProps }) => {
  const { Row, Data } = Table

  return (
    documentMetadata.map(doc => {
      return (
        <Row {...tableProps} key={doc.id}>
          <Data>{formatDate(doc.creationDate)}</Data>
          <Data>
            <DocumentNameWrapper>
              {doc.name}
            </DocumentNameWrapper>
          </Data>
          <DocumentDownloadButton fileName={doc.name} docId={doc.id} />
        </Row>)
    })
  )
}

From my DocumentDownloadButton I've created a function to download the file taking those two props to download onclick. The problem is it's downloading the file on refresh even before I've opened the panel which is where the click event happens

const DocumentDownloadButton = ({ docId, fileName }) => {
  const { apiFor } = useAxios()

  const downloadDocument = (id, file) => {
    apiFor('someApi')
      .get(`/documents/${id}`, { responseType: 'blob' }, {
        headers: {
          Accept: 'applicaton/octet-stream'
        } })
      .then((response) => {
        // add loading state
        const contentType = response.headers['content-type'] || 'application/octet-stream'
        const blob = new Blob([response.data], { type: contentType })
        return FileSaver.saveAs(blob, file)
      })
      .catch((error) => {
        console.error(error)
      })
  }

  return (
    <>
      <DownloadIconContainer onClick={downloadDocument(docId, fileName)}>
        <DownloadIconSmall />
      </DownloadIconContainer>
    </>
  )
}

Solution

  • That's because you're invoking the download function immediately rather than passing a reference of the function to the onClick. This should give you the intended behavior:

    const DocumentDownloadButton = ({ docId, fileName }) => {
      const { apiFor } = useAxios()
    
      const downloadDocument = (id, file) => {
        apiFor('someApi')
          .get(`/documents/${id}`, { responseType: 'blob' }, {
            headers: {
              Accept: 'applicaton/octet-stream'
            } })
          .then((response) => {
            // add loading state
            const contentType = response.headers['content-type'] || 'application/octet-stream'
            const blob = new Blob([response.data], { type: contentType })
            return FileSaver.saveAs(blob, file)
          })
          .catch((error) => {
            console.error(error)
          })
      }
    
      return (
        <>
          <DownloadIconContainer onClick={() => downloadDocument(docId, fileName)}>
            <DownloadIconSmall />
          </DownloadIconContainer>
        </>
      )
    }