React-pdf: Halt Render of PDF w/ Large Dataset until Download Button Clicked (Slow Initial Load Time)

Created on 22 Oct 2019  路  1Comment  路  Source: diegomura/react-pdf

Is your feature request related to a problem? Please describe.
I have a situation where I am passing data from a Redux state to be available for download into a PDF. When I try to access my Page that has the "Export to PDF" with Gatsby's Link component, if the dataset that I am passing in is large, it takes a while to load the page. I understand this comes from the fact that the full page won't load until the PDF component itself renders, even though there is no render required until the <PDFDownloadLink> button is pressed (Export to PDF). For example, below when I click "View Saved Questions" in the first screenshot below, it goes to the saved-questions page which includes my PDF component nested inside a PDFDownloadLink, but it will take a longer time to load the saved-questions page if there are more "questions" saved (larger data set).

Page that Links to Saved Questions Page
Screen Shot 2019-10-22 at 2 46 21 AM

Saved Questions Page
Screen Shot 2019-10-22 at 2 46 26 AM

Describe the solution you'd like
I propose that a PDF component that is only being used for download purposes should not be rendered until the <PDFDownloadLink> button is pressed (in my case, the Export to PDF button). It would be really nice to have the button change it's inner content to Loading X% and display progress to the user in terms of how long it will take the PDF document to render (from 0% to 100% and then set back to original inner content, e.g. "Export to PDF"). In situations where the PDF is only needed for download purposes, I think this would be very helpful for people generating Large PDFs with a lot of data.

Additional context
Here are my saved-questions page and PdfDocument component (rendered inside of the saved-questions page and set as a prop for the <PDFDownloadLink> component. Happy to provide any additional context if necessary.

saved-questions (gatsby page)

import React, { Component } from "react"
import uuid from "uuid"
import moment from "moment"

import { PDFDownloadLink } from "@react-pdf/renderer"
import { PdfDocument } from "../components/pdf"

import { connect } from "react-redux"
import { removeQuestion, clearAllQuestions } from "../state/app"

import Layout from "../components/layout"
import SEO from "../components/seo"
import PageLink from "../components/pagelink"
import Card from "../components/card"

import "../components/pdf.scss"

class SavedQuestions extends Component {
  unsaveQuestion = question => {
    this.props.removeQuestion(question)
  }

  clearAllQuestions = () => {
    this.props.clearAllQuestions()
  }

  render() {
    let { savedQuestions: questions } = this.props

    let questionList = questions.map((question, i) => (
      <>
        <li key={uuid.v4()}>
          <span
            unselectable="on"
            className="badge ml-8 hoverable"
            onClick={this.unsaveQuestion.bind(this, question)}
          >
            UNSAVE
          </span>
          <p>
            <b>{question.question}</b>
          </p>
          <p>{question.answer}</p>
        </li>
      </>
    ))

    return (
      <Layout location={this.props.location} crumbLabel="Practice">
        <SEO title="馃 Saved Questions" />
        <PageLink to="/" emoji="馃彔" label="Home" title="Back Home" />
        <PageLink
          to="/practice"
          emoji="馃憦"
          label="Clap"
          title="Back to Practice"
        />
        <div className="header-2">Saved Questions ({questions.length})</div>
        <Card
          label="surprise"
          emoji="馃槻"
          content={
            <div>
              <p>
                Use the <b>Export to PDF</b> button to download a list of your
                saved questions as a PDF.
              </p>
              <p>
                Please note that saved questions will <b>vanish</b> when you{" "}
                <b>leave</b> or <b>refresh</b> the website. However, you can
                safely navigate within the website without losing your
                questions.
              </p>
            </div>
          }
        />
        <div className="content">
          {questions.length === 0 ? (
            "No questions saved."
          ) : (
            <div className="content btn-ghost" onClick={this.clearAllQuestions}>
              Unsave All Questions
            </div>
          )}
          <ol>{questionList}</ol>
        </div>
        {questions.length > 0 && (
          <PDFDownloadLink
            document={<PdfDocument data={questions} />}
            fileName={
              "hackib-questions-" +
              moment()
                .format("MMM-Do-YY-h-mm-ss-a")
                .toLowerCase() +
              ".pdf"
            }
            className="content btn download-pdf"
          >
            {({ blob, url, loading, error }) =>
              loading ? "PDF Download Unavailable" : "Export to PDF"
            }
          </PDFDownloadLink>
        )}
      </Layout>
    )
  }
}

const mapStateToProps = state => {
  return { savedQuestions: state.app.savedQuestions }
}

export default connect(
  mapStateToProps,
  { removeQuestion, clearAllQuestions }
)(SavedQuestions)

PDF Component

import React from "react"
import {
  Page,
  Text,
  View,
  Document,
  Link,
  StyleSheet,
  Font,
} from "@react-pdf/renderer"
import moment from "moment"

const FONT = "IBM Plex Sans"
const FONT_URL =
  "https://fonts.gstatic.com/s/ibmplexsans/v7/zYXgKVElMYYaJe8bpLHnCwDKhdXeFaxOedfTDw.woff2"

Font.register({
  family: FONT,
  src: FONT_URL,
})

const styles = StyleSheet.create({
  page: {
    backgroundColor: "#ffffff",
    padding: 50,
    fontFamily: "Helvetica",
  },

  headerLayout: {
    textAlign: "center",
  },

  header: {
    fontSize: 16,
    fontFamily: "Helvetica-Bold",
  },

  question: {
    marginBottom: 12.5,
  },

  questionText: {
    fontFamily: "Helvetica-Bold",
  },

  answerText: {
    paddingLeft: 10,
  },

  content: {
    fontSize: 12,
    marginBottom: 10,
  },
})

const WEBSITE_URL = "" // commenting out for purpose of this post

export function PdfDocument({ data }) {
  return (
    <Document>
      <Page size="A4" style={styles.page}>
        <View style={styles.headerLayout}>
          <Text style={[styles.content, styles.header]}>
            HackIB Exported Questions
          </Text>
          <Text style={styles.content}>
            <Link src={WEBSITE_URL}>HackIB Website</Link>
          </Text>
          <Text style={styles.content}>
            {moment().format("MMMM Do YYYY, h:mm:ss a")}
          </Text>
        </View>
        {data.map((question, i) => (
          <View style={styles.question}>
            <Text style={[styles.content, styles.questionText]}>
              {i + 1 + ". " + question.question}
            </Text>
            <Text style={[styles.content, styles.answerText]}>
              {question.answer}
            </Text>
          </View>
        ))}
      </Page>
    </Document>
  )
}
new feature

Most helpful comment

I agree this feature would be really helpful. There is a similar (or duplicate) request at #736, and I think this would also resolve issues noted in #420

>All comments

I agree this feature would be really helpful. There is a similar (or duplicate) request at #736, and I think this would also resolve issues noted in #420

Was this page helpful?
0 / 5 - 0 ratings