Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PDFDownloadLink should generate pdf on click #736

Closed
tomasmatulis0 opened this issue Oct 14, 2019 · 25 comments · Fixed by diegomura/react-pdf-site#116
Closed

PDFDownloadLink should generate pdf on click #736

tomasmatulis0 opened this issue Oct 14, 2019 · 25 comments · Fixed by diegomura/react-pdf-site#116

Comments

@tomasmatulis0
Copy link

tomasmatulis0 commented Oct 14, 2019

Is your feature request related to a problem? Please describe.
It is an improvement. At the moment, if you use 'PDFDownloadLink' the PDF is being generated as the component loads.

Describe the solution you'd like
It is not mandatory, but having multiple heavy PDFs ready to be downloaded wouldn't be the best approach since not every user will need it.

Describe alternatives you've considered
I've used pdf() function to generate the blob and file-saver lib to download it:

import { saveAs } from 'file-saver';
import { pdf } from '@react-pdf/renderer';
import PdfDocument from '../PdfDocument';

const generatePdfDocument = async (documentData) => {
        const blob = await pdf((
            <PdfDocument
                title='My PDF'
                pdfDocumentData={documentData}
            />
        )).toBlob();
        saveAs(blob, fileName);
};

export default generatePdfDocument;```
@rishipr
Copy link

rishipr commented Oct 22, 2019

@tomasmatulis0 have you seen substantial performance gains from the alternative method you posted with file-saver? In the alternative provided, does this stop the PDF from being generated as the component loads?

@tomasmatulis0
Copy link
Author

@rishipr not quite, I just try to think ahead as it is a good thing to do and having n number of PDFs just ready to be downloaded feels like a waste of resources. Most of the times the user won't even need to download the PDF so it will be just laying there most of the time. I believe it would be an improvement in rare cases when you have more and heavier PDFs on the same page (this would be the way to test it). But again, I believe thinking ahead and saving resources is a good practice.

@idrisadetunmbi
Copy link

does the pdf function mean the pdf document is not generated on the fly and only generates/downloads when the generatePdfDocument is called (possibly fixing #420 and #608)?

@tomasmatulis0
Copy link
Author

@idrisadetunmbi not sure about fixing those two problems, but yes using pdf you can generate PDF file blob and download it on generatePdfDocument call. This way we can avoid generating PDF on fly and do it when needed.

@idrisadetunmbi
Copy link

Thanks @tomasmatulis0, that makes sense. Solves that problem for me and I think this should be the default.

@mdodge-ecgrow
Copy link

mdodge-ecgrow commented Dec 27, 2019

What is documentData in OP's example code? I am assuming that < PdfDocument /> is:

import React from 'react';
import {
	Document,
	Page,
	Text,
	View,
	StyleSheet
} from '@react-pdf/renderer';

// Create styles
const styles = StyleSheet.create({
	page: {
		flexDirection: 'row',
		backgroundColor: '#E4E4E4'
	},
	section: {
		margin: 10,
		padding: 10,
		flexGrow: 1
	}
});

// Create Document Component
const PdfDocument = () => (
	<Document>
		<Page size="A4" style={styles.page}>
			<View style={styles.section}>
				<Text>Section #1</Text>
			</View>
			<View style={styles.section}>
				<Text>Section #2</Text>
			</View>
		</Page>
	</Document>
);

export default PdfDocument;

@tomasmatulis0
Copy link
Author

@mdodge-ecgrow amm... documentData is just any kind of dynamic data that is passed to the component and add to the pdf (like data got from the API etc.).

@mdodge-ecgrow
Copy link

That makes sense, thank you.

@Pat1420
Copy link

Pat1420 commented Feb 24, 2020

Nice alternative , thanks tomasmatulis0. Works very well in addition with a spinner state , it's perfect for my letter generation.
const generateLetterPdf = async () => {
setLoadingLetter(true);
const blob = await pdf(
<UserWelcomeLetter customerInfo={customerInfo} />
).toBlob();
setLoadingLetter(false);
saveAs(blob, "Lettre_bienvenue_" + customerInfo.contactName + ".pdf");
};

@dance-dance-banana-frenzy

The OP's work-around with blob and saveAs works for me. In my case, I needed to wire up the PDF to a button, and only render it after the end-user clicked it. Thanks!

I also found that with the download link, it doesn't change when the parameters to the PDF document changes. It seems like the initial document is cached. Therefore I was always getting a blank PDF page without any results, because the first time my page is displayed, the parameters are just an empty array resulting in a PDF document without any data

@cmnstmntmn
Copy link

hmm, i'm trying the code snippet above, but for some reason it trows an err

import React from "react";
import { saveAs } from "file-saver";
import { pdf, Document, Page } from "@react-pdf/renderer";

const generatePDFDocument = async () => {
  const blob = await pdf(
    <Document>
      <Page>// My document data</Page>
    </Document>
  ).toBlob();

  console.log(blob);

  saveAs(blob, "pageName");
};

export default generatePDFDocument;
Uncaught (in promise) TypeError: Cannot read property 'slice' of undefined

@Pat1420
Copy link

Pat1420 commented Jul 8, 2020

hmm, i'm trying the code snippet above, but for some reason it trows an err

import React from "react";
import { saveAs } from "file-saver";
import { pdf, Document, Page } from "@react-pdf/renderer";

const generatePDFDocument = async () => {
  const blob = await pdf(
    <Document>
      <Page>// My document data</Page>
    </Document>
  ).toBlob();

  console.log(blob);

  saveAs(blob, "pageName");
};

export default generatePDFDocument;
Uncaught (in promise) TypeError: Cannot read property 'slice' of undefined

Hi, we don't have the full picture there .. if slice is coming from an object defined asynchronously , it needs to be initalized for the first render.. otherwise you might use yourObject?.slice to get undefined returned instead of an error..

@cmnstmntmn
Copy link

cmnstmntmn commented Jul 9, 2020

@Pat1420 that's very curious because i have no other promises. it's just a click event that's calling generatePDFDocument

              <button onClick={() => generatePDFDocument("doc name")}>
                download
              </button>

@cmnstmntmn
Copy link

there's also this issue

@layeng
Copy link

layeng commented Oct 15, 2020

Hi I also have the same issue. It takes 8sec to render a 300 record data. The page will freeze for 8 sec now until the rendering is complete. I can't image how many min it will freeze if the record runs into thousands. If you know of any way to overcome this, please let me know.

@mh0101
Copy link

mh0101 commented Feb 2, 2021

Is your feature request related to a problem? Please describe.
It is an improvement. At the moment, if you use 'PDFDownloadLink' the PDF is being generated as the component loads.

Describe the solution you'd like
It is not mandatory, but having multiple heavy PDFs ready to be downloaded wouldn't be the best approach since not every user will need it.

Describe alternatives you've considered
I've used pdf() function to generate the blob and file-saver lib to download it:

import { saveAs } from 'file-saver';
import { pdf } from '@react-pdf/renderer';
import PdfDocument from '../PdfDocument';

const generatePdfDocument = async (documentData) => {
        const blob = await pdf((
            <PdfDocument
                title='My PDF'
                pdfDocumentData={documentData}
            />
        )).toBlob();
        saveAs(blob, fileName);
};

export default generatePdfDocument;```

Great support - this has helped a lot

@rhadamez
Copy link

Hi I also have the same issue. It takes 8sec to render a 300 record data. The page will freeze for 8 sec now until the rendering is complete. I can't image how many min it will freeze if the record runs into thousands. If you know of any way to overcome this, please let me know.

Have you solved it? because I'm facing this, takes too long to generate the PDF to download, I'm running out of options.

@tomasmatulis0
Copy link
Author

Hi I also have the same issue. It takes 8sec to render a 300 record data. The page will freeze for 8 sec now until the rendering is complete. I can't image how many min it will freeze if the record runs into thousands. If you know of any way to overcome this, please let me know.

Have you solved it? because I'm facing this, takes too long to generate the PDF to download, I'm running out of options.

I mean, there are a bunch of examples how to solve this above. It's an async function, and if you don't want for your page to freeze, just don't await it, it will complete on it's own, and you will be able to use the page in the mean time 🤷‍♂️

@rhadamez
Copy link

rhadamez commented Jul 1, 2021

Hi I also have the same issue. It takes 8sec to render a 300 record data. The page will freeze for 8 sec now until the rendering is complete. I can't image how many min it will freeze if the record runs into thousands. If you know of any way to overcome this, please let me know.

Have you solved it? because I'm facing this, takes too long to generate the PDF to download, I'm running out of options.

I mean, there are a bunch of examples how to solve this above. It's an async function, and if you don't want for your page to freeze, just don't await it, it will complete on it's own, and you will be able to use the page in the mean time

Interesting, but maybe this don't work, cause it needs to process the PDF anyway. The approach I used to solve was using the generation of the PDF in the backend. But now that you mention it, how to let this process synchronous? maybe I didn't use in the correct way.

@tomasmatulis0
Copy link
Author

Hi I also have the same issue. It takes 8sec to render a 300 record data. The page will freeze for 8 sec now until the rendering is complete. I can't image how many min it will freeze if the record runs into thousands. If you know of any way to overcome this, please let me know.

Have you solved it? because I'm facing this, takes too long to generate the PDF to download, I'm running out of options.

I mean, there are a bunch of examples how to solve this above. It's an async function, and if you don't want for your page to freeze, just don't await it, it will complete on it's own, and you will be able to use the page in the mean time

Interesting, but maybe this don't work, cause it needs to process the PDF anyway. The approach I used to solve was using the generation of the PDF in the backend. But now that you mention it, how to let this process synchronous? maybe I didn't use in the correct way.

Do you use generatePdfDocument function like ... await generatePdfDocument(documentData) or just generatePdfDocument(documentData)?

@benoitkopp
Copy link

you're a god

@fabiank0
Copy link

fabiank0 commented Aug 2, 2021

Thanks, this function also solved a problem for me where PDFDownloadLink would not include custom fonts.

@josegoval
Copy link

josegoval commented Oct 1, 2021

Why do not to create a custom component or functionality for it in this library?

export const downloadPDFAsynchronously = (componentToRender: Document, filename: string) => {...}

@maitedev
Copy link

Hello, here I bought a solution that worked for me, I hope it helps you.
`import React, { Component } from "react";
import { connect } from "react-redux";
import { pdf } from "@react-pdf/renderer";
import { saveAs } from "file-saver";

class PDFViewerButton extends Component {
handleDownloadPDF = async () => {
try {
const responseData = await this.props.getData({
activeObject: "rx",
searchParameters: {
service_id: service_id,
patient_id: patient_id,
},
});
const foundObjects = responseData.data?.foundObjects || [];
if (foundObjects.length > 0) {
return foundObjects;
} else {
this.props.updateModalVisibilityShow("modal_alert", {
title: "No Data Found",
message:
"We're sorry, but there is no data available to generate the report at the moment.",
});
}
} catch (error) {
console.error("Error fetching data:", error);
}
};

render() {
return (
<Button
onClick={async () => {
try {
const data = await this.handleDownloadPDF();
if (data) {
const doc = (

);
const fileName = somename.pdf;
const asPdf = pdf([]);
asPdf.updateContainer(doc);
const blob = await asPdf.toBlob();
saveAs(blob, fileName);
// Open the PDF in a new tab
const pdfUrl = URL.createObjectURL(blob);
window.open(pdfUrl, "_blank");
URL.revokeObjectURL(pdfUrl);
}
} catch (error) {
console.error("Error generating PDF:", error);
}
}}
>
PRINT

);
}
}`

@naveenApplikation
Copy link

without await how can i save the blob value ? i want to download the pdf getting page unresponsive error . i tried above method . but for download the pdf you where not using web worker in the above example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.