Skip to content

Error: Cannot read properties of null (reading 'sendWithPromise') #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
michaelcychan opened this issue Apr 16, 2025 · 4 comments
Open

Comments

@michaelcychan
Copy link

I am encounter an error: Error: Cannot read properties of null (reading 'sendWithPromise') (Image below)

Initial render is correct. However, when the Parent component updates the props.pdfURL, I would encounter the error. In addition, the error is not caught by RPProvider.onLoadError, hence the Error state did not show up.

If I leave any empty dependency array in the useEffect, I do not encounter the error, but the viewer only shows the old PDF file without updating.

This is my PDF viewer component

const ReactPDFViewer = (props: Props) => {
  const [errorLoading, setErrorLoading] = useState(false);
  const [objectUrl, setObjectUrl] = useState("");

  // making src separate for easier use
  const providerProps = useMemo<Omit<RPProviderProps, "src">>(() => ({
    ...defaultProviderProps,
    ...props.providerProps,
  }),[])

  const layoutProps = useMemo<RPLayoutProps>(() => ({
    ...defaultLayoutProps,
    ...props.defaultLayoutProps,
  }),[])

  useEffect(() => {
    setObjectUrl(props.pdfURL);
  }, [props.pdfURL]);

  if (!objectUrl || errorLoading) {
    return <DocumentPageErrorState />;
  }

  return (
    <RPProvider
      src={objectUrl}
      {...providerProps}
      onLoadError={(error) => {
        console.warn(error);
        setErrorLoading(true);
      }}
    >
      <RPDefaultLayout {...layoutProps}>
        <RPPages />
      </RPDefaultLayout>
    </RPProvider>
  );
};

Image of the error:
Image

@michaelcychan
Copy link
Author

I found a way to bypass the issue, which is to add key={objectUrl} to RPProvider, forcing the whole Reader to re-render. It might not be ideal as the reader should handle a change of src gracefully, but I think it can be temporary fix.

@david-reactpdf
Copy link
Contributor

david-reactpdf commented Apr 18, 2025

@michaelcychan

Thanks for sharing this—and great find on the workaround! 🙌

After looking into it, the issue seems to stem from the fact that React PDF attempts to initialize the page instance before the document instance has fully finished loading. Your approach of adding key={objectUrl} to RPProvider does force a full re-render, which helps avoid the timing conflict—so yes, it works as a solid temporary fix.

That said, we’ll definitely look into how we can better handle dynamic src updates more gracefully within the library itself. Appreciate you flagging this and I will let you know when we have updates.

@david-reactpdf
Copy link
Contributor

@michaelcychan

Upon further investigation, it looks like this issue happens when React PDF is rendered on the server side. In a framework like Next.js, the viewer component should be loaded using dynamic() with ssr: false, like so:

import dynamic from 'next/dynamic';

const ReactPDFViewer = dynamic(() => import('./ReactPDFViewer'), {
  ssr: false,
});

This ensures the viewer is only initialized in the browser, which avoids hydration mismatches and issues like the sendWithPromise error.

You can also find more information about using React PDF with Next.js in our documentation here (https://docs.react-pdf.dev/introduction/basic-usage#basic-implementation)[https://docs.react-pdf.dev/introduction/basic-usage#basic-implementation]

Could you give that a try and let me know if it resolves the issue?

@michaelcychan
Copy link
Author

@david-reactpdf Thank you for your investigation. However in my case, I have always been using client side to render the PDF Viewer.

I have a RenderPDFWrapper.tsx

"use client";

import dynamic from "next/dynamic";

export const LazyReactPDFViewer = dynamic(() => import("./ReactPDFViewer"), {
  ssr: false,
});

to wrap around my ReactPDFViewer.tsx

"use client";

import React, { useState, useEffect, useMemo } from "react";
import {
  RPProvider,
  RPDefaultLayout,
  RPPages,
  ZoomLevel,
  RPProviderProps,
  RPLayoutProps,
} from "@pdf-viewer/react";
import DocumentPageErrorState from "../DocumentPageErrorState";

//... 
export default function ReactPDFViewer(props: Props) {
  const [errorLoading, setErrorLoading] = useState(false);
  const [objectUrl, setObjectUrl] = useState("");

  // making src separate for easier use
  const providerProps = useMemo<Omit<RPProviderProps, "src">>(
    () => ({
      ...defaultProviderProps,
      ...props.providerProps,
    }),
    [props.providerProps]
  );

  const layoutProps = useMemo<RPLayoutProps>(
    () => ({
      ...defaultLayoutProps,
      ...props.defaultLayoutProps,
    }),
    [props.defaultLayoutProps]
  );

  useEffect(() => {
    setErrorLoading(false);
    setObjectUrl(props.url);
  }, [props.url]);

  if (!objectUrl || errorLoading) {
    return <DocumentPageErrorState />;
  }

  return (
    // key is essential to force re-rendering of the component
    // without re-rendering, it will cause an error: Error: Cannot read properties of null (reading 'sendWithPromise')
    // possibly caused by an underlying dependency, pdfjs-dist, as the same error occurs in other pdf viewers with same dependency
    <RPProvider
      key={props.url}
      src={objectUrl}
      {...providerProps}
      onLoadError={(error) => {
        console.warn(error);
        setErrorLoading(true);
      }}
    >
      <RPDefaultLayout {...layoutProps}>
        <RPPages />
      </RPDefaultLayout>
    </RPProvider>
  );
}

So when I use the Component, I would do something like this:

return (
      <Suspense fallback={<DocumentLoadingState />}>
        <LazyReactPDFViewer url={url} />
      </Suspense>
  );

I hope this is enough to ensure the PDF to be rendered at client side. Please me know if I missed anything

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants