-
-
Notifications
You must be signed in to change notification settings - Fork 221
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
How to use React Suspense to wait for content to load #469
Comments
Hello, glad you're enjoying the library, thank you! I would highly suggest staying away from Suspense right not for a production website unless you can't find another way of solving your problem. The Suspense API is very experimental, and is likely to see massive breaking changing in React 18. Also, you say that you are "using Suspense for some of our data fetching," but Suspense does not perform data fetching. Are you using Relay? With that being said, my comment in the other issue was saying that you could prevent rendering of the entire react-to-print stack until all your components had loaded. So for example, something like: const response = fetchData();
return (
<Suspense>
<PrintingView />
</Suspense>
<NormalView />
) That's a very contrived example, and I've never myself actually used Suspense with react-to-print, but that's how I imagined it would look like. |
Thank you for your reply! I was a bit unclear in my question, sorry about that. We are using I tried your example this morning, and I couldn't get it to work. Quick code example: const componentRef = useRef(null);
const [isPrinting, setIsPrinting] = useState(false);
const handlePrint = useReactToPrint({
content: () => componentRef.current,
onBeforeGetContent: () => setIsPrinting(true),
onAfterPrint: () => setIsPrinting(false),
});
return (
<button onClick={handlePrint}>
{isPrinting && (
<div style={{ display: 'none' }}>
<React.Suspense fallback={null}>
<PrintablePriceBoard id={id} ref={componentRef} />
</React.Suspense>
</div>
)}
{children}
</button>
); The As I mentioned, we have a working implementation using callbacks and avoiding React Suspense, so it is no must for us to use Suspense for this, but in case anyone else is having issues with this, I thought it was worth asking about :) I'd be happy to test anything else if you have any ideas on how I could make it work, but for now I think I'll just stick with our callback solution and move forwards. Thank you again! 😄 |
Thanks for the clarifications! So, what you need is for const componentRef = useRef(null);
const [isPrinting, setIsPrinting] = useState(false);
const getContentRef = useRef(null);
const handlePrint = useReactToPrint({
content: () => componentRef.current,
onBeforeGetContent: () => {
return new Promise((resolve) => {
getContentRef.current = resolve;
setIsPrinting(true);
});
},
onAfterPrint: () => setIsPrinting(false),
});
useEffect(() => {
// I don't actually know if checking the componentRef.current will work here the
// way I think it will, but I feel like this check should be good enough
if (isPrinting && getContentRef.current && componentRef.current) {
getContentRef.current(); // Resolves the promise passed to `onBeforeGetContent`
}
}, [isPrinting, getContentRef.current, componentRef.current]);
return (
<button onClick={handlePrint}>
{isPrinting && (
<div style={{ display: 'none' }}>
<React.Suspense fallback={null}>
<PrintablePriceBoard id={id} ref={componentRef} />
</React.Suspense>
</div>
)}
{children}
</button>
); |
Hi again! I have been digging a little into this this morning, and I have learned something new and have gotten this to work for us in a good way (in my opinion, at least). Your example did not work out of the box, and it seems the problem was not really with Suspense, it was with the use of refs. It seems that one shouldn't rely on The comment links to an explanation of using const componentRef = useRef(null);
const onBeforeGetContentResolve = useRef<(() => void) | null>(null);
const [isPrinting, setIsPrinting] = useState(false);
const handlePrint = useReactToPrint({
content: () => componentRef.current,
onBeforeGetContent: () => {
return new Promise((resolve) => {
setIsPrinting(true);
onBeforeGetContentResolve.current = resolve;
});
},
onAfterPrint: () => setIsPrinting(false),
});
const callbackRef = useCallback((node) => {
if (node !== null && onBeforeGetContentResolve.current !== null) {
componentRef.current = node;
onBeforeGetContentResolve.current();
}
}, []);
return (
<button onClick={handlePrint}>
<div style={{ display: 'none' }}>
{isPrinting && (
<React.Suspense fallback={null}>
<PrintablePriceBoard id={id} ref={callbackRef} />
</React.Suspense>
)}
</div>
{children}
</button>
); The PrintablePriceBoard component now gets its data in a totally self-contained way using recoiljs, suspense and fetch which also makes it really easy to render it wherever I want in the application while developing so that we can tweak the design of it without having to actually click it and see the print preview :) I would have preferred to not use a second Again, thank you so much for your help and your awesome library! |
Thanks for reporting back! That totally makes sense about refs, and the I like the idea of |
Thank you for the great library! In our application we use a callback to wait for content to load before printing it, and it works fine. We are, however, also using Suspense for some of our data fetching, and I am curious as to how we would use Suspense and this library together. It was mentioned in another issue that it can be done (#440 (comment)), but when searching for it, I couldn't find any info on how to actually do it. Does anyone have any pointers on how we would use Suspense to elegantly wait for a child component to fetch all its data before printing it?
The text was updated successfully, but these errors were encountered: