Skip to content

Commit d7016ff

Browse files
committed
feat: clean additional google recaptcha script on unmount
1 parent 8ea9f99 commit d7016ff

File tree

3 files changed

+66
-23
lines changed

3 files changed

+66
-23
lines changed

README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,17 @@ The complete documentation to the enterprise version you can see [here](https://
3737

3838
`GoogleReCaptchaProvider`'s responsibility is to load the necessary reCaptcha script and provide access to reCaptcha to the rest of your application.
3939

40-
You can customize the injected `script` tag with the `scriptProps` prop. This prop allows you to add `async`, `defer`, `nonce` attributes to the script tag. You can also control whether the injected script will be added to the document body or head with `appendTo` attribute. Example can be found belows. The `scriptProps` and its attributes are all optional.
40+
Usually, your application only needs one provider. You should place it as high as possible in your React tree. It's to make sure you only have one instance of Google Recaptcha per page and it doesn't reload unecessarily when your components re-rendered.
4141

42-
It also provides an optional prop `language` to support different languages that is supported by Google Recaptcha.
43-
https://developers.google.com/recaptcha/docs/language
42+
Same thing applied when you use this library with framework such as Next.js or React Router and only want to include the script on a single page. Try to make sure you only have one instance of the provider on a React tree and to place it as high (on the tree) as possible.
4443

45-
The provider also provide the prop `useRecaptchaNet` to load script from `recaptcha.net`:
46-
https://developers.google.com/recaptcha/docs/faq#can-i-use-recaptcha-globally
44+
| **Props** | **Type** | **Default** | **Required?** | **Note** |
45+
| --------------- | :------: | ----------: | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46+
| reCaptchaKey | Boolean | | Yes | Your recaptcha key, get one from [here](https://www.google.com/recaptcha/intro/v3.html) |
47+
| scriptProps | Object | | No | You can customize the injected `script` tag with this prop. It allows you to add `async`, `defer`, `nonce` attributes to the script tag. You can also control whether the injected script will be added to the document body or head with `appendTo` attribute. |
48+
| language | String | | No | optional prop to support different languages that is supported by Google Recaptcha. https://developers.google.com/recaptcha/docs/language |
49+
| useRecaptchaNet | Boolean | false | No | The provider also provide the prop `useRecaptchaNet` to load script from `recaptcha.net`: https://developers.google.com/recaptcha/docs/faq#can-i-use-recaptcha-globally |
50+
| useEnterprise | Boolean | false | No | [Enterprise option](#enterprise) |
4751

4852
```javascript
4953
import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3';
@@ -57,7 +61,7 @@ ReactDom.render(
5761
scriptProps={{
5862
async: false, // optional, default to false,
5963
defer: false, // optional, default to false
60-
appendTo: "head", // optional, default to "head", can be "head" or "body",
64+
appendTo: 'head', // optional, default to "head", can be "head" or "body",
6165
nonce: undefined // optional, default undefined
6266
}}
6367
>

example/google-recaptcha-example.tsx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import React, { useState, FC, useCallback } from 'react';
1+
import React, { useState, FC, useCallback, useEffect } from 'react';
22
import { useGoogleReCaptcha } from '../src/use-google-recaptcha';
3-
import { GoogleReCaptcha } from '../src/google-recaptcha';
43

54
export const GoogleRecaptchaExample: FC = () => {
65
const { executeRecaptcha } = useGoogleReCaptcha();
@@ -20,14 +19,6 @@ export const GoogleRecaptchaExample: FC = () => {
2019
setNoOfVerifications(noOfVerifications => noOfVerifications + 1);
2120
}, [dynamicAction, executeRecaptcha]);
2221

23-
const handleReCaptchaVerify = useCallback(
24-
token => {
25-
setToken(token);
26-
setNoOfVerifications(noOfVerifications => noOfVerifications + 1);
27-
},
28-
[setNoOfVerifications, setToken]
29-
);
30-
3122
const handleTextChange = useCallback(event => {
3223
setActionToChange(event.target.value);
3324
}, []);
@@ -36,6 +27,20 @@ export const GoogleRecaptchaExample: FC = () => {
3627
setDynamicAction(actionToChange);
3728
}, [actionToChange]);
3829

30+
useEffect(() => {
31+
if (!executeRecaptcha || !dynamicAction) {
32+
return;
33+
}
34+
35+
const handleReCaptchaVerify = async () => {
36+
const token = await executeRecaptcha(dynamicAction);
37+
setToken(token);
38+
setNoOfVerifications(noOfVerifications => noOfVerifications + 1);
39+
};
40+
41+
handleReCaptchaVerify();
42+
}, [executeRecaptcha, dynamicAction]);
43+
3944
return (
4045
<div>
4146
<div>
@@ -48,10 +53,6 @@ export const GoogleRecaptchaExample: FC = () => {
4853
<br />
4954
{token && <p>Token: {token}</p>}
5055
<p> No of verifications: {noOfVerifications}</p>
51-
<GoogleReCaptcha
52-
action={dynamicAction}
53-
onVerify={handleReCaptchaVerify}
54-
/>
5556
</div>
5657
);
5758
};

src/utils.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ interface InjectGoogleReCaptchaScriptParams {
1313
};
1414
}
1515

16-
export const isScriptInjected = (scriptId: string) =>
17-
!!document.querySelector(`#${scriptId}`);
18-
16+
/**
17+
* Function to generate the src for the script tag
18+
*
19+
* @param param0
20+
* @returns
21+
*/
1922
const generateGoogleRecaptchaSrc = ({
2023
useRecaptchaNet,
2124
useEnterprise
@@ -30,6 +33,33 @@ const generateGoogleRecaptchaSrc = ({
3033
return `https://www.${hostName}/recaptcha/${script}`;
3134
};
3235

36+
/**
37+
* Function to clean the recaptcha_[language] script injected by the recaptcha.js
38+
*/
39+
const cleanGstaticRecaptchaScript = () => {
40+
const script = document.querySelector(
41+
`script[src^='https://www.gstatic.com/recaptcha/releases']`
42+
);
43+
44+
if (script) {
45+
script.remove();
46+
}
47+
};
48+
49+
/**
50+
* Function to check if script has already been injected
51+
*
52+
* @param scriptId
53+
* @returns
54+
*/
55+
export const isScriptInjected = (scriptId: string) =>
56+
!!document.querySelector(`#${scriptId}`);
57+
58+
/**
59+
* Function to clean google recaptcha script
60+
*
61+
* @param scriptId
62+
*/
3363
export const cleanGoogleRecaptcha = (scriptId: string) => {
3464
// remove badge
3565
const nodeBadge = document.querySelector('.grecaptcha-badge');
@@ -42,8 +72,16 @@ export const cleanGoogleRecaptcha = (scriptId: string) => {
4272
if (script) {
4373
script.remove();
4474
}
75+
76+
cleanGstaticRecaptchaScript();
4577
};
4678

79+
/**
80+
* Function to inject the google recaptcha script
81+
*
82+
* @param param0
83+
* @returns
84+
*/
4785
export const injectGoogleReCaptchaScript = ({
4886
reCaptchaKey,
4987
language,

0 commit comments

Comments
 (0)