react-pdf/rendererを使って、作成したpdfを新規タブに表示する
こちらはQiitaで書いた記事と同じ内容です。
https://qiita.com/powaaaaa/items/3c0b3aa08f53c0871fdf
ReactでPDFを作成し、それを新規タブに表示させる方法についてです。 react-pdfで検索した際に困ったため、記事にしてみようと思い立ちました。
バージョン
- vite: 4.4.5
- React: 18.2.0
- TypeScript: 5.0.2
- react-pdf/renderer: 3.1.15
コード
当初はchatGPTさんに手伝ってもらったものを改良して行きました。 ですが記事を書く際に公式ドキュメントを見ていると、BlobProviderなるものが提供されていたので、そちらを使用した例も自戒を込めて記載します。 ちゃんとドキュメント読んで
なお関数等の処理はカスタムフックとして他の処理と一緒に読み込もうとして少し苦戦したので、メモがてら以下コードもそのようになっております。
作成するPDF
適当に、一枚ずつ数字が書かれているpdfを作成します。
引数は[1, 2, 3]
のようなnumberの配列を取るようになっています。
PDF.tsximport { Document, Page, Text } from "@react-pdf/renderer"; export const Pdf = ({ data }: { data: number[] }) => ( <Document> {data.map((item) => ( <Page key={item}> <Text>{item}</Text> </Page> ))} </Document> );
pdfを使用した方法
index.tsximport { Pdf } from "./Pdf"; import { PrintButton } from "./PrintButton"; function App () { return <PrintButton element={<Pdf data={[1, 2, 3, 4, 5]} />} /> }; export default App;
PrintButton.tsximport { usePrintButton } from "./usePrintButton"; export const PrintButton = ({ element }: { element: JSX.Element }) => { const { handlePrintPDF } = usePrintButton(element); return <button onClick={handlePrintPDF}>印刷する</button>; };
usePrintButton.tsimport { pdf } from "@react-pdf/renderer"; type UsePrintButton = { handlePrintPDF: () => void; }; export const usePrintButton = (element: JSX.Element): UsePrintButton => { const handlePrintPDF = async () => { try { const blob = await pdf(element).toBlob(); const url = URL.createObjectURL(blob); window.open(url, "_blank"); } catch (error) { console.error("Error handing nameplate printing:", error); } }; return { handlePrintPDF }; };
解説
そもそもblobとは何ぞ
Blob = Binary Large OBject 通常のデータ型に格納できないバイナリデータを格納するためのデータ型です。
JavaScriptでも同様にバイナリデータを扱うオブジェクトとして使用できるそうです。
ちなみに、このBlobへのアクセス先をURLとして表示させることを「Blob URL Schema」と呼ぶそうです。
このURLを作成しているのが、ここではURL.createObjectURL(blob)
となります。
usePrintButton.tsについて
pdfを利用して、受け取ったエレメントのblobデータを作成しています。
それをURLcreateObjectURL
によりURLでアクセスできるようにし、window.open
メソッドにより新規タブで該当urlを開くようになっています。
BlobProviderを使用した方法
index.tsximport { UsingBlobProvider } from "./UsingBlobProvider"; function App() { return <UsingBlobProvider /> } export default App;
UsingBlobProvider.tsximport { BlobProvider } from "@react-pdf/renderer"; import { Pdf } from "./Pdf"; export const UsingBlobProvider = () => { const handlePrintPdf = (url: string | null) => { if (url) { const newWindow = window.open(url, "_blank"); } else { alert("URL is null"); } }; return ( <> <BlobProvider document={<Pdf data={[1, 2, 3, 4, 5, 6, 7, 8]} />}> {({ url }) => ( <div onClick={() => handlePrintPdf(url)}> <span>印刷する</span> </div> )} </BlobProvider> </> ); };
解説
BlobProvider
BlobProvider
のdocument
属性に作成したPDFを渡し、そのタグの中でchildrenであるurlをonClickの関数に渡しています。
こっちの方がスッキリしてますね()
childrenには他にもblobやloading, errorが使用出来るようです。
https://react-pdf.org/components#blobprovider
参考リンク
- BlobProvider
https://dev.to/jaymeeujj/how-to-generate-custom-pdf-using-react-and-react-pdf-6d4
- 公式ドキュメント
https://react-pdf.org/advanced#on-the-fly-rendering
- blobについて
目次