Skip to content

Instantly share code, notes, and snippets.

@SpudNyk
Created September 6, 2020 16:05
Show Gist options
  • Save SpudNyk/44b6602e4fc3c4d4d9197fb6b6876a44 to your computer and use it in GitHub Desktop.
Save SpudNyk/44b6602e4fc3c4d4d9197fb6b6876a44 to your computer and use it in GitHub Desktop.
react-pdf v2 hook based generator

An Implementation of @react-pdf generation using hooks instead of BlobProvider.

Includes an Implementation of PDF Viewer using the hook.

This version also support generating the react pdf document via a function rather than a direct child. If used the function can optionally be asynchronous.

import { useState, useEffect, useRef } from 'react';
import { pdf } from '@react-pdf/renderer';
const resolveDoc = (doc) => {
if (typeof doc === 'function') return doc();
return doc;
};
const generatePDF = async (document) => {
// note using a function is better so react will only use the PDF reconciler
// (no warnings about duplicate context etc.)
// support async root document generation
const docRoot = await resolveDoc(document);
const generator = pdf({});
generator.updateContainer(docRoot);
return generator.toBlob();
};
const revokeURL = (urlRef) => {
const url = urlRef.current;
if (url) {
urlRef.current = null;
URL.revokeObjectURL(url);
}
};
const usePDFGenerator = (document) => {
const [error, setError] = useState(null);
const [generating, setGenerating] = useState(true);
const blob = useRef(null);
const url = useRef(null);
useEffect(() => {
let cancelled = false;
setError(null);
setGenerating(true);
blob.current = null;
revokeURL(url);
// attempt to let browser update state while the pdf is being generated
// Maybe react-pdf could be updated to use a web-worker for layout and generation?
setTimeout(
() =>
generatePDF(document).then(
(pdfBlob) => {
// overwritten
if (!cancelled) {
blob.current = pdfBlob;
revokeURL(url);
url.current = URL.createObjectURL(pdfBlob);
setGenerating(false);
}
},
(error) => {
console.log('Error generating PDF', error);
setError(error);
setGenerating(false);
}
),
0
);
return () => {
// TODO workout how to actually cancel a running pdf generation
cancelled = true;
revokeURL(url);
};
}, [document]);
return { generating, error, url: url.current, blob: blob.current };
};
export default usePDFGenerator;
import React from 'react';
import PropTypes from 'prop-types';
import usePdfGenerator from './usePDFGenerator';
const Viewer = ({ children, innerRef, placeholder, ...props }) => {
const { generating, error, url } = usePdfGenerator(children);
return generating ? (
placeholder
) : error !== null ? (
'An error occurred'
) : (
<iframe src={url.current} ref={innerRef} {...props} />
);
};
Viewer.propTypes = {
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
font: PropTypes.string,
placeholder: PropTypes.node,
};
export default Viewer;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment