0

I'm trying to generate a PDF based on user input. The user can add, update, and delete items to a list. The list is a redux slice where the state is kept

Adding and updating are working but for some reason when I delete an item. I get the error Eo is not a function TypeError: Eo is not a function

My initial thought is that how the rendering process works, that the pdf isn't repainted but the existing pdf is changed. Not sure of if I'm in the right direction

PdfPreview.tsx

return (
    <PDFViewer key='testing' width="100%" height={window.innerHeight} showToolbar={false}>
        <DefaultPdf
        personaData={throttledPersonaData}
        secondaryColor={props.secondaryColor}
        experiences={throttledExperiencesData}
      />
    </PDFViewer>
  );

DefaultPdf.tsx

<View>
     <WorkPdf works={work}/>
</View>

Work.tsx

export const WorkPdf: React.FC<WorkPdfProps> = ({ works }: WorkPdfProps) => {
    const t = works.map((v, i) => {
        return (
            <Text key={i}>{v.role}</Text>
        )
    })
    return (
       <View>
            <Text >Work</Text>
            {t}
       </View>
    )
}

I can confirm: That the variables that are displayed in the pdf are all strings The array is empty array.

I would have expect that the pdf is repainted and so the items are updated (or in this case, deleted)

The library that is used is react-pdf.

    "@react-pdf/renderer": "^4.3.0",
    "react": "^19.1.0",
0

4 Answers 4

1

It is a rendering bug in react-pdf. I found that since it only occurs on delete, you can render the pdf in a new instance by setting key={x} on PDFViewer each time the data changes (clone your data object). This forces a render from scratch, avoiding the issue:


export const PDFPreview: React.FC<InvoicePDFPreviewProps> = ({
  formData,
}) => {
  const count = useRef(0);
  useEffect(() => {
    count.current++;
  }, [formData]);
  return (
    <div className="w-full h-[600px] border rounded-lg overflow-hidden">
      <PDFViewer width="100%" height="100%" key={count.current}>
        <InvoiceDocument formData={formData} />
      </PDFViewer>
    </div>
  );
};

then:
<PDFPreview formData={clonedFormData}} />
Sign up to request clarification or add additional context in comments.

2 Comments

This solution works well. You may need to apply this also for PDFDownloadLink component.
Dude, saved my bacon, one of my engineers was generating blobs to avoid the viewer, thanks a lot.
0

I had the same problem. When the rendered pdf is on screen and user delete an item, the app crashes. My hacky solution is to hide the pdf before re-rendering and display it back afterwards

  const removeStuff = () => {
    showPdf(() => false);

    updateState()

    setTimeout(() => {
      showPdf(() => true);
    }, 1);
  };

1 Comment

I tried this but it still doesn't work. I am pretty sure the component is not even mounted, at least useEffect with state as dependency did not fire when I update the state, but somehow the PDF is still connected to the states and as long as I save the state I get this error. and the PDF viewer is not even visible on the screen. How did you fix it?
0

So I did find another solution. Very simple but it does the job. I've added a status property in the object, when deleting the status is changing Then using styling{display: skill.status === 'delete' ? 'none' : 'flex'}

docs

2 Comments

I tried this but it still doesn't work. I am pretty sure the component is not even mounted, at least useEffect with state as dependency did not fire when I update the state, but somehow the PDF is still connected to the states and as long as I save the state I get this error. and the PDF viewer is not even visible on the screen. How did you fix it?
@DaysonD can you share some code?
0

you can add suspense key

import PrintTablePdf from "./tables/print-table-pdf";

const OwnerContent: React.FC<PageProps> = async ({ searchParams }) => {
  const cookieStore = await cookies();

  const search = searchParamsCacheOrder.parse(await searchParams);

  const [
    { data: dataCategorys },
    { data: dataOrders, total, error: errorOrder },
  ] = await Promise.all([
    fetchCategorys(cookieStore),
    getAllOrdersForSuperadmin(search),
  ]);

  if (!dataOrders || errorOrder) throw new Error(errorOrder);

  return (
    <Suspense
      key={dataOrders.length}
      fallback={
        <Button
          type="button"
          variant="outline"
          size="sm"
          className="flex items-center justify-between"
          disabled={true}
        >
          <Loader2 className="animate-spin" />
          Please wait
        </Button>
      }
    >
      <PrintTablePdf
        document={<TablePdfOrderOwner data={dataOrders} />}
        fileName="laporan-order.pdf"
        className="flex w-0 pb-2 pl-1"
      />
    </Suspense>
  );
};



"use client";

import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { DocumentProps, PDFDownloadLink } from "@react-pdf/renderer";
import { FileText, Loader2 } from "lucide-react";
import type { ReactElement } from "react";

export default function PrintTablePdf({
  document,
  trigger,
  fileName = "laporan-order.pdf",
  className,
}: {
  document: ReactElement<DocumentProps>;
  trigger?: React.ReactNode; 
  fileName?: string;
  className?: string;
}) {
  return (
    <PDFDownloadLink
      document={document}
      fileName={fileName}
      className={cn(className)}
    >
      {({ loading }) =>
        loading ? (
          <Button
            type="button"
            variant="outline"
            size="sm"
            className="flex items-center justify-between"
            disabled={true}
          >
            <Loader2 className="animate-spin" />
            Please wait
          </Button>
        ) : (
          trigger || (
            <Button
              type="button"
              variant="outline"
              size="sm"
              className="flex items-center justify-between"
            >
              <FileText />
              Cetak PDF
            </Button>
          )
        )
      }
    </PDFDownloadLink>
  );
}

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.