react-pdf icon indicating copy to clipboard operation
react-pdf copied to clipboard

PDFViewer does not render until reloaded

Open nimeshmaharjan1 opened this issue 11 months ago • 3 comments

Describe the bug

  • I have used the <PDFViewer> to display the PDF in the web app but on the first render the PDFViewer is not shown.

  • I have to reload sometimes even multiple times

  • I have a condition that checks if the data from the API has arrived before rendering the DOM so I dont think its because of that.

  • If there are any loading states that I am missing do let me know

  • Below is the code snippet

	if (
		!queryResult.data ||
		queryResult.isLoading ||
		!userData ||
		isLoading ||
		!window
	)
		return <LoadingScreen />;

	return (
		<div className="w-screen min-h-screen bg-white grid text-black">
			<Container maxWidth="md" className="py-6 px-0 h-full">
				

				<PDFViewer height={600} width={"100%"}>
					<ReportPDF
						pdfTitle={props.reportTitle}
						reportTitle={props.reportTitle}
						data={parsedData}
						tableHead={TABLE_HEAD}
						user={{
							address: userData.address ? CombineAddress(userData.address) : "",
							avatar: userData.avatar,
							name: ConstructFullName(
								userData.firstName,
								userData.middleName,
								userData.lastName
							),
							role: role.key,
						}}
						companyInfo={{
							name:
								role.key === "superadmin"
									? "ABC"
									: (role.role?.companyName ?? ""),
							logo:
								(role.key === "superadmin"
									? LOGOS.MMD
									: role.role?.companyLogo) ?? LOGOS.MMD,
						}}
					/>
				</PDFViewer>
			</Container>
		</div>
	);

Screenshots

First Render =>

Image

After Reloading =>

Image

Desktop (please complete the following information):

  • OS: Mac, Windows, Linux
  • Browser Chrome
  • React-pdf version 7.7.3
  • @react-pdf/renderer": "^3.4.5

nimeshmaharjan1 avatar Feb 20 '25 14:02 nimeshmaharjan1

It looks like your PDFViewer component isn't rendering properly on the first load, requiring multiple reloads. Since you already have a condition ensuring data availability before rendering, the issue might be related to:

  1. Delayed API Response / Query Inconsistencies

    • Even though queryResult.isLoading is checked, sometimes React Query updates state asynchronously, and queryResult.data might still be undefined momentarily.
    • Try adding a console log to verify the state before rendering:
      console.log("Query Result:", queryResult);
      console.log("User Data:", userData);
      console.log("Window:", typeof window !== "undefined");
      
  2. window Check & SSR Issues

    • If you're using Next.js or any SSR framework, the window object is only available on the client side.
    • Try ensuring that PDFViewer is rendered only on the client:
      import dynamic from "next/dynamic";
      
      const PDFViewer = dynamic(() => import("@react-pdf/renderer").then((mod) => mod.PDFViewer), { ssr: false });
      
  3. Force Re-render on Data Change

    • The PDFViewer might not be updating properly when data arrives.
    • Try forcing a re-render using useEffect and useState:
      const [readyToRender, setReadyToRender] = useState(false);
      
      useEffect(() => {
        if (queryResult.data && userData) {
          setReadyToRender(true);
        }
      }, [queryResult.data, userData]);
      
      if (!readyToRender) return <LoadingScreen />;
      
  4. Check for ReportPDF Issues

    • Ensure parsedData, TABLE_HEAD, and user are not undefined before passing them to ReportPDF.
    • Wrap ReportPDF inside a useMemo hook to avoid unnecessary re-renders:
      const memoizedPDF = useMemo(() => (
        <ReportPDF
          pdfTitle={props.reportTitle}
          reportTitle={props.reportTitle}
          data={parsedData}
          tableHead={TABLE_HEAD}
          user={{
            address: userData.address ? CombineAddress(userData.address) : "",
            avatar: userData.avatar,
            name: ConstructFullName(
              userData.firstName,
              userData.middleName,
              userData.lastName
            ),
            role: role.key,
          }}
          companyInfo={{
            name:
              role.key === "superadmin"
                ? "ABC"
                : (role.role?.companyName ?? ""),
            logo:
              (role.key === "superadmin"
                ? LOGOS.MMD
                : role.role?.companyLogo) ?? LOGOS.MMD,
          }}
        />
      ), [parsedData, userData, role, props.reportTitle]);
      
      return (
        <PDFViewer height={600} width={"100%"}>
          {memoizedPDF}
        </PDFViewer>
      );
      

Final Steps to Debug:

  • Check console logs to verify state changes.
  • Use useEffect to track queryResult.data updates.
  • Try delaying rendering slightly using setTimeout(() => setReady(true), 500);
  • Ensure that React-PDF dependencies are correctly installed.

Souravkkt0404 avatar Mar 04 '25 13:03 Souravkkt0404

It looks like your PDFViewer component isn't rendering properly on the first load, requiring multiple reloads. Since you already have a condition ensuring data availability before rendering, the issue might be related to:

  1. Delayed API Response / Query Inconsistencies

    • Even though queryResult.isLoading is checked, sometimes React Query updates state asynchronously, and queryResult.data might still be undefined momentarily.
    • Try adding a console log to verify the state before rendering:
      console.log("Query Result:", queryResult);
      console.log("User Data:", userData);
      console.log("Window:", typeof window !== "undefined");
      
  2. window Check & SSR Issues

    • If you're using Next.js or any SSR framework, the window object is only available on the client side.
    • Try ensuring that PDFViewer is rendered only on the client:
      import dynamic from "next/dynamic";
      
      const PDFViewer = dynamic(() => import("@react-pdf/renderer").then((mod) => mod.PDFViewer), { ssr: false });
      
  3. Force Re-render on Data Change

    • The PDFViewer might not be updating properly when data arrives.
    • Try forcing a re-render using useEffect and useState:
      const [readyToRender, setReadyToRender] = useState(false);
      
      useEffect(() => {
        if (queryResult.data && userData) {
          setReadyToRender(true);
        }
      }, [queryResult.data, userData]);
      
      if (!readyToRender) return <LoadingScreen />;
      
  4. Check for ReportPDF Issues

    • Ensure parsedData, TABLE_HEAD, and user are not undefined before passing them to ReportPDF.
    • Wrap ReportPDF inside a useMemo hook to avoid unnecessary re-renders:
      const memoizedPDF = useMemo(() => (
        <ReportPDF
          pdfTitle={props.reportTitle}
          reportTitle={props.reportTitle}
          data={parsedData}
          tableHead={TABLE_HEAD}
          user={{
            address: userData.address ? CombineAddress(userData.address) : "",
            avatar: userData.avatar,
            name: ConstructFullName(
              userData.firstName,
              userData.middleName,
              userData.lastName
            ),
            role: role.key,
          }}
          companyInfo={{
            name:
              role.key === "superadmin"
                ? "ABC"
                : (role.role?.companyName ?? ""),
            logo:
              (role.key === "superadmin"
                ? LOGOS.MMD
                : role.role?.companyLogo) ?? LOGOS.MMD,
          }}
        />
      ), [parsedData, userData, role, props.reportTitle]);
      
      return (
        <PDFViewer height={600} width={"100%"}>
          {memoizedPDF}
        </PDFViewer>
      );
      

Final Steps to Debug:

  • Check console logs to verify state changes.
  • Use useEffect to track queryResult.data updates.
  • Try delaying rendering slightly using setTimeout(() => setReady(true), 500);
  • Ensure that React-PDF dependencies are correctly installed.

Souravkkt0404 avatar Mar 04 '25 13:03 Souravkkt0404

It looks like your PDFViewer component isn't rendering properly on the first load, requiring multiple reloads. Since you already have a condition ensuring data availability before rendering, the issue might be related to:

  1. Delayed API Response / Query Inconsistencies

    • Even though queryResult.isLoading is checked, sometimes React Query updates state asynchronously, and queryResult.data might still be undefined momentarily.
    • Try adding a console log to verify the state before rendering: console.log("Query Result:", queryResult); console.log("User Data:", userData); console.log("Window:", typeof window !== "undefined");
  2. window Check & SSR Issues

    • If you're using Next.js or any SSR framework, the window object is only available on the client side.

    • Try ensuring that PDFViewer is rendered only on the client: import dynamic from "next/dynamic";

      const PDFViewer = dynamic(() => import("@react-pdf/renderer").then((mod) => mod.PDFViewer), { ssr: false });

  3. Force Re-render on Data Change

    • The PDFViewer might not be updating properly when data arrives.

    • Try forcing a re-render using useEffect and useState: const [readyToRender, setReadyToRender] = useState(false);

      useEffect(() => { if (queryResult.data && userData) { setReadyToRender(true); } }, [queryResult.data, userData]);

      if (!readyToRender) return <LoadingScreen />;

  4. Check for ReportPDF Issues

    • Ensure parsedData, TABLE_HEAD, and user are not undefined before passing them to ReportPDF.

    • Wrap ReportPDF inside a useMemo hook to avoid unnecessary re-renders: const memoizedPDF = useMemo(() => ( <ReportPDF pdfTitle={props.reportTitle} reportTitle={props.reportTitle} data={parsedData} tableHead={TABLE_HEAD} user={{ address: userData.address ? CombineAddress(userData.address) : "", avatar: userData.avatar, name: ConstructFullName( userData.firstName, userData.middleName, userData.lastName ), role: role.key, }} companyInfo={{ name: role.key === "superadmin" ? "ABC" : (role.role?.companyName ?? ""), logo: (role.key === "superadmin" ? LOGOS.MMD : role.role?.companyLogo) ?? LOGOS.MMD, }} /> ), [parsedData, userData, role, props.reportTitle]);

      return ( <PDFViewer height={600} width={"100%"}> {memoizedPDF} </PDFViewer> );

Final Steps to Debug:

  • Check console logs to verify state changes.
  • Use useEffect to track queryResult.data updates.
  • Try delaying rendering slightly using setTimeout(() => setReady(true), 500);
  • Ensure that React-PDF dependencies are correctly installed.

Thank you very much for the detailed reply so basically what I did was the loading state that we get from the PDFDownloadLink component i stored it in a local state and used it as a loading indicator for the pdf as well which solved the issue

nimeshmaharjan1 avatar Mar 05 '25 08:03 nimeshmaharjan1