-
Notifications
You must be signed in to change notification settings - Fork 338
Description
I have a use case where I want users to be able to click a link to download a QR code shown via the <QRCodeCanvas> component. My first attempt was to get a data URL from the canvas via toDataURL() in a ref callback, and set that as the href on a link.
function MyApp() {
const [ dataUrl, setDataUrl ] = useState('');
const canvasRefCallback = useCallback((canvas) => {
if (!canvas) {
return;
}
setDataUrl(canvas.toDataURL());
}, []);
return (
<div>
<QRCodeCanvas
ref={canvasRefCallback}
value={window.location.href}
/>
{dataUrl &&
<a href={dataUrl} download="qrcode">Download QR Code</a>
}
</div>
);
}This unfortunately results in a transparent PNG because the <canvas> element is rendered and the ref callback is invoked before the useEffect() that draws the QR code into the canvas has run.
As a workaround, I'm having users click on a button first where I then create a link dynamically and call .click() on it to initiate the download. This is fine, but it's a little silly making users click on a button, so that I can create a link and call .click() on it, when the users themselves could just click on that link.
function MyApp() {
const canvasRef = useRef();
const downloadLinkRef = useRef();
const onDownloadQRCode = useCallback(() => {
const canvas = canvasRef.current;
const donwloadLink = downloadLinkRef.current;
if (!canvas || !downloadLink) {
return;
}
downloadLink.href = canvas.toDataURL();
downloadLink.download = 'qrcode';
downloadLink.click();
}, []);
return (
<div>
<QRCodeCanvas
ref={canvasRef}
value={window.location.href}
/>
<button onClick={onDownloadQRCode}>Download QR code</button>
<a hidden ref={downloadLinkRef} />
</div>
);
}Since drawing to the canvas is synchronous, I think the first example could work if <QRCodeCanvas> took a onComplete callback prop that it called at the end of the useEffect() after drawing the QR code has been completed.
function MyApp() {
const [ dataUrl, setDataUrl ] = useState('');
const canvasRef = useRef();
const onDrawQRCodeComplete = useCallback(() => {
const canvas = canvasRef.current;
if (!canvas) {
return;
}
setDataUrl(canvas.toDataURL());
}, []);
return (
<div>
<QRCodeCanvas
onComplete={onDrawQRCodeComplete}
ref={canvasRef}
value={window.location.href}
/>
{dataUrl &&
<a href={dataUrl} download="qrcode">Download QR Code</a>
}
</div>
);
}I'd be happy to open a PR if this is something everyone is open to.