react-pdf/rendererを使って、作成したpdfを新規タブに表示する

TypeScript

React

こちらは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.tsx
import { 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.tsx
import { Pdf } from "./Pdf"; import { PrintButton } from "./PrintButton"; function App () { return <PrintButton element={<Pdf data={[1, 2, 3, 4, 5]} />} /> }; export default App;
PrintButton.tsx
import { usePrintButton } from "./usePrintButton"; export const PrintButton = ({ element }: { element: JSX.Element }) => { const { handlePrintPDF } = usePrintButton(element); return <button onClick={handlePrintPDF}>印刷する</button>; };
usePrintButton.ts
import { 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.tsx
import { UsingBlobProvider } from "./UsingBlobProvider"; function App() { return <UsingBlobProvider /> } export default App;
UsingBlobProvider.tsx
import { 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

BlobProviderdocument属性に作成した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について

https://memo.ag2works.tokyo/post-4332/

https://qiita.com/azu369yu/items/8998e1e1536a5acfb7b3

powwa

@c624fdabca951804

目次