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

is it possible to print component without rendering it in the dom

Open shadesOfcodes opened this issue 2 years ago • 1 comments

I have this paginated table, i want to print the entire entries ( table without pagination ), while only the paginated table is in view...

shadesOfcodes avatar Mar 20 '22 13:03 shadesOfcodes

You can use the onBeforeGetContent callback to load the rest of the data into the component before printing:

const [data, setData] = useState(initialData);
const componentRef = useRef(null);
const onBeforeGetContentResolve = useRef(null);

useReactToPrint({
  onBeforeGetContent = () => {
    return new Promise((resolve) => {
      fetchMyData.then((d) => {
        setData(d);
        onBeforeGetContentResolve = resolve;
      });
    });
  },
  onAfterPrint(() => {
    setData(initialData); // Set `data` back to its pre-print value when printing is over
  }),
});

useEffect(() => {
  // Resolve the Promise once we know data has loaded into the state
  if (onBeforeGetContentResolve.current && data.length > initialData.length) {
    onBeforeGetContentResolve.current();
  }
}, [data]);

return <div>{data.map(d) => <div key={d.id}>{d.value}</div>}</div>;

MatthewHerbst avatar Mar 20 '22 18:03 MatthewHerbst

My implementation, creating a component that handles printing loading the component just before printing:

import { useRef, useEffect, useState } from 'react';
import ReactToPrint from 'react-to-print';

import PrintDownloadButton from 'components/PrintDownloadButton';

/**
 * Print Component Without Loading Into View
 * @param {object} props
 * @param {JSX.Element} props.content
 * @param {string} props.triggerLabel
 * @returns {JSX.Element}
 */
export default function PrintAsync({
  content,
  triggerLabel,
}) {
  const [isPrinting, setIsPrinting] = useState(false);
  const [isStartedPrinting, setIsStartPrinting] = useState(false);

  const printRef = useRef(null);

  // We store the resolve Promise being used in `onBeforeGetContent` here
  const promiseResolveRef = useRef(null);

  // We watch for the state to change here, and for the Promise resolve to be available
  useEffect(() => {
    if (isPrinting && promiseResolveRef.current) {
      // Resolves the Promise, letting `react-to-print` know that the DOM updates are completed
      promiseResolveRef.current();
    }
  }, [isPrinting]);

  return (
    <>
      <ReactToPrint
        trigger={(buttonProps) => <PrintDownloadButton label={triggerLabel} {...buttonProps} />}
        content={() => printRef.current}
        onBeforeGetContent={() => {
          setIsStartPrinting(true)
          return new Promise((resolve) => {
            promiseResolveRef.current = resolve;
            setIsPrinting(true);
          });
        }}
        onAfterPrint={() => {
          // Reset the Promise resolve so we can print again
          promiseResolveRef.current = null;
          setIsPrinting(false);
          setIsStartPrinting(false);
        }}
        bodyClass="p-4"
      />
      <div style={{ display: 'none' }}>{isStartedPrinting ? content(printRef) : null}</div>
    </>
  );
}

Usage:

<PrintAsync
  key="1"
  triggerLabel="Print"
  content={(ref) => (
    <CustomComponent
      ref={ref}
      dynamicData={{}}
    />
  )}
/>

BossBele avatar Apr 11 '24 14:04 BossBele