I'd like to pass a ref to a dynamically loaded component. When the component's loaded with a regular import everything works as expected. When using a dynamic import the ref value is undefined || { current: null }
.
I get the following the error message in the console:
Warning: Function components cannot be given refs.
Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
I've attempted to use a React.forwardRef
but have been unsuccessful in my attempts.
Parent Component: Recaptcha.tsx
import React from 'react';
import loadable from '@loadable/component';
import ReCAPTCHA from 'react-google-recaptcha';
export type RecaptchaProps = {
handleOnError: () => void;
handleOnExpire: () => void;
forwardedRef: React.MutableRefObject<ReCAPTCHA | null>;
error: string | null;
load: boolean;
};
const Recaptcha: React.FunctionComponent<RecaptchaProps> = ({
handleOnError,
handleOnExpire,
error,
forwardedRef,
// load,
}) => {
const RecaptchaPackage = loadable(
() => import('components/Form/Recaptcha/RecaptchaPackage'),
);
return (
<div>
<RecaptchaPackage
handleOnError={handleOnError}
handleOnExpire={handleOnExpire}
forwardedRef={forwardedRef}
/>
</div>
);
};
export default React.memo(Recaptcha);
Dynamic Component: RecaptchaPackage.tsx
import React from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
export type RecaptchaPackageProps = {
handleOnError: () => void;
handleOnExpire: () => void;
forwardedRef: React.MutableRefObject<ReCAPTCHA | null>;
};
const RecaptchaPackage: React.FunctionComponent<RecaptchaPackageProps> = ({
handleOnError,
handleOnExpire,
forwardedRef,
}) => {
return process.env.GATSBY_RECAPTCHA_PUBLIC ? (
<ReCAPTCHA
onErrored={handleOnError}
onExpired={handleOnExpire}
ref={forwardedRef}
sitekey={process.env.GATSBY_RECAPTCHA_PUBLIC}
size="invisible"
/>
) : null;
};
export default React.memo(RecaptchaPackage);
You're looking to pass a ref of the ReCAPTCHA component (from 'react-google-recaptcha') up through two parent elements. For this you can make use of React's forwardRef
.
Another thing to note is that the ref will not always be assigned a value on component mount, this is true when using with and without dynamic loading. So if you want to log the ref for testing, log in a setTimeout
or some other event that is triggered later.
Your Recaptcha component would look like this:
import React from "react";
import loadable from "@loadable/component";
import ReCAPTCHA from "react-google-recaptcha";
export type RecaptchaProps = {
handleOnError: () => void;
handleOnExpire: () => void;
error: string | null;
load: boolean;
};
const RecaptchaPackage = loadable(() => import("./RecaptchaPackage"));
const Recaptcha: React.FunctionComponent<RecaptchaProps> = React.forwardRef(({
handleOnError,
handleOnExpire,
error,
// load,
}, ref) => {
return (
<div>
<RecaptchaPackage
ref={ref}
handleOnError={handleOnError}
handleOnExpire={handleOnExpire}
/>
</div>
);
})
export default React.memo(Recaptcha);
And your RecaptchaPackage component would look like this:
import React from "react";
import ReCAPTCHA from "react-google-recaptcha";
export type RecaptchaPackageProps = {
handleOnError?: () => void;
handleOnExpire?: () => void;
};
const RecaptchaPackage: React.FunctionComponent<RecaptchaPackageProps> = React.forwardRef(({
handleOnError,
handleOnExpire
}, ref) => {
return process.env.GATSBY_RECAPTCHA_PUBLIC ? (
<ReCAPTCHA
ref={ref}
onErrored={handleOnError}
onExpired={handleOnExpire}
sitekey={process.env.GATSBY_RECAPTCHA_PUBLIC}
size="invisible"
/>
) : null);
});
export default React.memo(RecaptchaPackage);
I've created a sandbox that validates that this should work: https://playcode.io/972680