Render recaptcha script only inside the Recaptcha component
farskid opened this issue · 7 comments
For now, one should add the Google re-captcha script into the project's HTML and add it globally in order for re-captcha to be rendered fully.
If we could load the script dynamically when the Recaptcha
component mounts, that would be really awesome since the script is always the same and Using script
tag in JSX won't work properly.
For rendering only once, one could use:
class LazyLoadedRecaptcha extends React.Component {
componentDidMount() {
const script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.defer = true;
script.src = "https://www.google.com/recaptcha/api.js"
this.refs.addScriptHere.appendChild(script);
}
render() {
return (<div class="lazy-recaptcha" ref="addScriptHere" />);
}
}
This would solve the issue mentioned on #147 as well.
Would be happy to send the PR!
Please clarify how to use it! I have this problem. but do not understand this solution!
add this in class you add the recaptcha
.
.
.
componentDidMount() {
const script = document.createElement("script");
script.src = "https://www.google.com/recaptcha/api.js";
script.async = true;
document.body.appendChild(script);
}
.
.
.
@dann1609 The proposal is simple. Whatever component you're using the recaptcha in, you could add the mentioned script to it's componentDidMount
lifecycle method, so that only when that component mounts, the script referenced to recaptcha API will be added to the component's HTML and will load it.
This way, you don't need to add the whole script as a global to your HTML and it won't load when it's not necessary.
@dann1609 This will load multiple copies of the script each time the user navigates to that component. You would need to do something with global application state to check existence.
@andrew-goldie You're right. This code was written when I had an early experience with React.
I'd prefer to do this by creating a one time Provider wrapper around my whole application. something like this:
function RecaptchaProvider({children}) {
return class extends React.Component {
componentDidMount() {
// Logic here
}
render(){ return children; }
}
}
With this approach, since there is no state changes on top of RecaptchaProvider and all changes are happening inside the app which is render as children
, the recaptcha script will only be added once.
also, make sure to add StateProviders such as Redux's provider components inside children of this RecaptchaProvider.
If you want the script to only load once per page, I recommend using a technique like this:
getInitialState() {
@alreadyLoaded = false
...
componentDidMount() {
if(!@alreadyLoaded){
@alreadyLoaded=true
const script = document.createElement("script");
script.src = "https://www.google.com/recaptcha/api.js";
script.async = true;
document.body.appendChild(script);
}
}
This is not an issue to load the google script by ourselves.
My setup include a service to load the script:
const init = () => {
const script = document.createElement('script');
script.async = true;
script.defer = true;
script.src = 'https://www.google.com/recaptcha/api.js';
document.body.appendChild(script);
};
export default {
init
};
And then in my form using react hooks:
// ...some imports...
import Recaptcha from 'react-recaptcha';
import ReCaptchaService from 'services/recaptcha';
const Form = ({...some props..., handleSubmit}) => {
let recaptcha = useRef(null);
const [isCaptchaInit, setCaptchaInit] = useState(false);
const handleCaptchaExpired = () => {
recaptcha.reset();
};
const handleCaptchaToken = (token: string) => {
console.log(token);
handleSubmit();
};
const handleValidateBeforeSubmit = evt => {
evt.preventDefault();
if (isCaptchaInit) {
recaptcha.reset();
} else {
setCaptchaInit(true);
}
recaptcha.execute();
};
useEffect(() => {
ReCaptchaService.init();
}, []);
return (
<form ... onSubmit={handleValidateBeforeSubmit}>
...
<Recaptcha
ref={e => (recaptcha = e)}
expiredCallback={handleCaptchaExpired}
hl={locale}
sitekey={YOUR_SITE_KEY}
size="invisible"
verifyCallback={handleCaptchaToken}
/>
<button type="submit">
Submit
</button>
</form>
);
};
export default Form;
This is working fine.
Hope this help :)