本記事の内容について
ポートフォリオ「MATRIXFLOW」の機能拡充としてpdf作成機能を実装しリリースしたので、本記事に投稿いたします!
実装Before/After
実装前
マトリクスの横にあるサイドバー風ドキュメントに、マトリクスの内容が自動で反映されます。

実装後
サイドバー風ドキュメント部分にpdfプレビューが表示され、ダウンロードできるようにしました。

実装方法について
使用技術
- React 18.3.1
- Redux
 - react-pdf/renderer 4.0.0
 
 - Vite 5.4.8
 
実装手順
react-pdf/renderer のインストール
公式を参考にreact-pdf/rendererをインストールします。
npmを使っているため下記コマンドにて
npm install @react-pdf/renderer --save
公式のサンプルpdfを表示できるようにする
まず、公式のサンプルpdfと同じ内容をアプリ上で表示できるかトライしました。
pdfを表示するページコンポーネントCreateMatrixFlowPage.jsx上の設定
- 
PDFViewerを設定 - pdfとして表示するコンポーネントを
DocumentPDFコンポーネントとする 
import React from 'react';
// PDFViewerコンポーネントをreact-pdf/rendererからインポート
import { PDFViewer } from '@react-pdf/renderer';
// コンポーネントの読み込み
import DocumentPDF from '../Components/DocumentPDF';
const CreateMatrixFlowPage = () => {
    return (
        <>
            <div className="sidebar-on-PDFViewerMode">
                <PDFViewer style={{ width: '100%', height: '80vh' }}>
                    // pdf表示したいコンポーネント(DocumentPDF)をPDFViewerでラップ
                    <DocumentPDF/>
                </PDFViewer>
            </div>
        </>
    );
};
export default CreateMatrixFlowPage;
DocumentPDFコンポーネントの設定
公式のサンプルpdfに対してImageコンポーネントを除外した内容をDocumentPDFコンポーネントへ
公式のサンプルpdfは下記のように画像パスが設定されていますが、MATRIXFLOWでも同様にこのパスに画像ファイルを配置しないとエラーになるため除外しました。
<Image
  style={styles.image}
  src="/images/quijote1.jpg"
/>
import React from 'react';
import { Page, Text, Font, View, Document, StyleSheet } from '@react-pdf/renderer';
const DocumentPDF = () => (
  <Document>
    <Page style={styles.body}>
      <Text style={styles.header} fixed>
        ~ Created with react-pdf ~
      </Text>
      <Text style={styles.title}>Don Quijote de la Mancha</Text>
      <Text style={styles.author}>Miguel de Cervantes</Text>
      // ..省略(公式のサンプルpdf内容とほぼ同様、Imageは除外)
    </Page>
  </Document>
);
Font.register({
  family: 'Oswald',
  src: 'https://fonts.gstatic.com/s/oswald/v13/Y_TKV6o8WovbUd3m_X9aAA.ttf'
});
const styles = StyleSheet.create({
  body: {
    paddingTop: 35,
    paddingBottom: 65,
    paddingHorizontal: 35,
  },
  title: {
    fontSize: 24,
    textAlign: 'center',
    fontFamily: 'Oswald'
  },
  author: {
    fontSize: 12,
    textAlign: 'center',
    marginBottom: 40,
  },
  subtitle: {
    fontSize: 18,
    margin: 12,
    fontFamily: 'Oswald'
  },
  text: {
    margin: 12,
    fontSize: 14,
    textAlign: 'justify',
    fontFamily: 'Times-Roman'
  },
  image: {
    marginVertical: 15,
    marginHorizontal: 100,
  },
  header: {
    fontSize: 12,
    marginBottom: 20,
    textAlign: 'center',
    color: 'grey',
  },
  pageNumber: {
    position: 'absolute',
    fontSize: 12,
    bottom: 30,
    left: 0,
    right: 0,
    textAlign: 'center',
    color: 'grey',
  },
});
export default DocumentPDF;
公式サンプルのpdfが表示されることを確認
公式サンプルのpdfが表示されることを確認。(ここまでは順調...だった...)

バグ対処
公式サンプルpdfが表示できて順調と思いきや、いろいろバグ対処に見舞われたので記します...😭
バグ対処①:Reduxから参照した内容をpdfに表示できない
実装した内容
サイドバー風のドキュメントは、Reduxを使ってstoreに保持された状態を参照した内容を表示しています。
DocumentPDFコンポーネントに対して同様にReduxによりstoreの状態を参照しようとしました。
コードは次のようにしました。
import React from 'react';
import { useSelector } from 'react-redux';
import { Page, Text, Font, View, Document, StyleSheet } from '@react-pdf/renderer';
const DocumentPDF = () => {
 // Reduxからworkflowsを参照
      const workflows = useSelector((state) => state.workflows.workflow);
  // Reduxから参照したworkflowsからなる定数
      const workflowName = workflows[0]?.name;
  <Document>
    <Page style={styles.body}>
      <Text style={styles.header} fixed>
        ~ Created with react-pdf ~
      </Text>
      // Reduxから参照したworkflowsからworkflowNameが表示できるかトライ
      <Text style={styles.title}>{workflowName}</Text>
    </Page>
  </Document>
};
Font.register({
  family: 'Oswald',
  src: 'https://fonts.gstatic.com/s/oswald/v13/Y_TKV6o8WovbUd3m_X9aAA.ttf'
});
const styles = StyleSheet.create({
// ...(省略)
});
export default DocumentPDF;
バグ発生
...がエラー発生(下記)によりReduxから参照できませんでした。
CreateMatrixFlowPage-0nmkX1Hh.js:218 TypeError: Cannot destructure property 'store' of 't(...)' as it is null.
対処
Reduxに参照するのではなく、親コンポーネントから参照したい内容をpropsとして渡すことでエラー発生せず表示することができました。
import React from 'react';
import { useSelector } from 'react-redux';
import { PDFViewer } from '@react-pdf/renderer';
const CreateMatrixFlowPage = () => {
    // DocumentPDFコンポーネントに表示したいstateをReduxから参照
        const flowsteps = useSelector((state) => state.flowsteps);
        const checklists = useSelector((state) => state.checkLists);
        const workflows = useSelector((state) => state.workflow);
        const workflowId = useSelector((state) => state.workflow.workflowId);
    return (
        <div>
            <PDFViewer style={{ width: '100%', height: '80vh' }}>
                <DocumentPDF
                  // propsとしてDocumentPDFコンポーネントに渡す
                    flowsteps={flowsteps}
                    checklists={checklists}
                    workflowId={workflowId}
                    workflows={workflows}
                />
            </PDFViewer>
        </div>
    );
};
export default CreateMatrixFlowPage;
バグ対処②:日本語で入力した内容が文字化けする
実装内容
DocumentPDFコンポーネントに、pdfのタイトルに日本語をハードコーディング
(タイトルとして「申請」)
import React from 'react';
import { useSelector } from 'react-redux';
import { Page, Text, Font, View, Document, StyleSheet } from '@react-pdf/renderer';
const DocumentPDF = () => {
  <Document>
    <Page style={styles.body}>
      <Text style={styles.header} fixed>
        ~ Created with react-pdf ~
      </Text>
      // 試しにタイトルとして「申請」と表示させたい
      <Text style={styles.title}>申請</Text>
    </Page>
  </Document>
};
Font.register({
  family: 'Oswald',
  src: 'https://fonts.gstatic.com/s/oswald/v13/Y_TKV6o8WovbUd3m_X9aAA.ttf'
});
const styles = StyleSheet.create({
// ...(省略)
});
export default DocumentPDF;
バグ発生
対処
- kashimuraryo /font-nasuからNasuフォントをダウンロード
 - Nasuフォントを保存
 
import React from 'react';
import { Page, Text, Font, View, Document, StyleSheet } from '@react-pdf/renderer';
// Nasuフォントのインポート
import fontRegular from '../../assets/fonts/Nasu-Regular.ttf'; //Nasuフォントを保存したパスを指定
import fontBold from '../../assets/fonts/Nasu-Bold.ttf'; //Nasuフォントを保存したパスを指定        
const DocumentPDF = () => {
  return (
    <Document>
      <Page style={styles.body}>
        <Text style={styles.title}>申請</Text>
      </Page>
    </Document>
  );
};
// Nasuフォントを登録
Font.register({
  family: 'Nasu-Regular',
  src: fontRegular
});
Font.register({
  family: 'Nasu-Bold',
  src: fontBold
});
// Nasuフォントを指定
const styles = StyleSheet.create({
  body: { padding: 30 },
  title: { 
      fontSize: 24, textAlign: 'center', marginBottom: 10, 
      fontFamily: 'Nasu-Bold'  //font-familyに'Nasu-Bold'を指定
  },
});
export default DocumentPDF;
参考記事
下記の記事を参考にNasuフォントを使用いたしました。
終わりに
自分のスタイリングしたサイドバー風ドキュメントだと、本格的なドキュメントができている感じがあまりしないなぁ、と物足りなかったのですがpdf作成機能を実装して本格的に近づき嬉しいです!
もう1つのポートフォリオ「PlotForge」について
もう1つポートフォリオ「PlotForge」もリリースしております!
こちらもご覧いただけますと幸いです。
