Skip to content
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

"react-to-print" did not receive a contentRef #724

Closed
jnsereko opened this issue Aug 15, 2024 · 7 comments
Closed

"react-to-print" did not receive a contentRef #724

jnsereko opened this issue Aug 15, 2024 · 7 comments

Comments

@jnsereko
Copy link

jnsereko commented Aug 15, 2024

Description

@MatthewHerbst I am not able to print content using react-to-print's 3.0.0-beta-1.
This is because i have no control of the OptionalContent that i literally don't supply from my code.
As a result, the contentRef element passed to useReactToPrint is not picked up hence erroring

Error

Screenshot 2024-08-15 at 18 47 23
level:"warning",messages:['"react-to-print" received a `contentRef` option and a optional-content param passed to its callback. The `contentRef` option will be ignored.']}),o()):e?e.current:void t({messages:['"react-to-print" did not receive a `contentRef` option or a optional-content param pass to its callback.'],suppressErrors:n})}({contentRef:r,optionalContent:e,suppressErrors:d});if(!u)return void t({messages:["There is nothing to print"]

My code

const PrintComponent: React.FC<PrintComponentProps> = ({ closeModal, patient }) => {
  const contentToPrintRef = useRef<HTMLDivElement>(null);
  const onBeforeGetContentResolve = useRef<() => void | null>(null);
  const [isPrinting, setIsPrinting] = useState(false);
  const headerTitle = "Some Title";

  useEffect(() => {
    if (isPrinting && onBeforeGetContentResolve.current) {
      onBeforeGetContentResolve.current();
    }
  }, [isPrinting]);

  const handleBeforeGetContent = useCallback(
    () => {
      return new Promise<void>((resolve) => {
        if (patient && headerTitle) {
          onBeforeGetContentResolve.current = resolve;
          setIsPrinting(true);
        }
      });
    }, [headerTitle, patient],
  );

  const handleAfterPrint = useCallback(() => {
    onBeforeGetContentResolve.current = null;
    setIsPrinting(false);
    closeModal();
  }, [closeModal]);

  const handlePrintError = useCallback((errorLocation, error) => {
    onBeforeGetContentResolve.current = null;
    console.log('An error occurred in "{{errorLocation}}":  { errorLocation } + error');
    setIsPrinting(false);
  }, []);

  const handlePrint = useReactToPrint({
    contentRef: contentToPrintRef,
    documentTitle: `${headerTitle}`,
    onAfterPrint: handleAfterPrint,
    onBeforePrint: handleBeforeGetContent,
    onPrintError: handlePrintError,
  });

  return (
    <>
      <ModalHeader
        closeModal={closeModal}
        title=Print Stuff
      />
      <ModalBody>
        
      </ModalBody>
      <ModalFooter>
        <Button className={styles.some_styles} disabled={isPrinting} onClick={handlePrint} kind="primary">
          {isPrinting ? (
            <InlineLoading className={styles.some_styles} description={'Printing ' + '...'} />
          ) : (
            Print
          )}
        </Button>
      </ModalFooter>
      <div className={`${styles.some_styles}`}>
          <MyComponent
            ref={contentToPrintRef}
          />
      </div>
    </>
  );
};

cc @ibacher @denniskigen @pirupius

@ibacher
Copy link

ibacher commented Aug 15, 2024

@jnsereko I fixed this in your PR. The issue is that handlePrint is passed directly to onClick. onClick calls the supplied function with the event as the first argument, which handlePrint here interprets as "additionalContent" because that's what it's first argument is. The solution is easy: onClick={() => handlePrint()}

@jnsereko
Copy link
Author

The solution is easy: onClick={() => handlePrint()}

I feel silly right now.
Are the react-to-print examples also showing this? Seams they do

@MatthewHerbst feel free to reopen this in case of anything else

@MatthewHerbst
Copy link
Owner

Hey friends, I was traveling the last few days so just seeing this now. Previously the function returned by useReactToPrint took the event as it's first parameter and additional content was passed as the second parameter. We never actually did anything with the event though so I removed it from v3. I wonder if it would make life easier for folks if we keep it around so this "workaround" isn't necessary

@ibacher
Copy link

ibacher commented Aug 19, 2024

I wonder if it would make life easier for folks if we keep it around so this "workaround" isn't necessary

It is a nice convenience, I think!

@MatthewHerbst
Copy link
Owner

The only downside is that the event is currently typed as unknown, meaning if someone were to put the optionalContent into the first prop instead of the event there wouldn't be a type error. Let me take a look at this later tonight and see if I can come up with a nice solution

@ibacher
Copy link

ibacher commented Aug 19, 2024

Could it be as easy as something like:

handlePrint(event?: unknown, content?: UseReactToPrintHookContent) {
  const contentHook === content ?? typeof event === "function" ?  event : undefined;
  // do stuff
};

I think this still works for the onClick handler, but also if the user submits a function as the first argument. It's not as type-safe as it might be.

@MatthewHerbst
Copy link
Owner

MatthewHerbst commented Aug 19, 2024

Yeah, an overload like that is what I'm imagining as well, make it super hard to screw up. I was imagining doing something along the lines of instanceof Event though not totally sure if that always works in React land with synthetic events. The is function approach you have above might be best!

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

3 participants