@gahojin-inc/react-google-recaptcha 2025.8.1 → 2025.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +36 -7
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.mts +3 -1
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useState } from "react";
|
|
1
|
+
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/utils.ts
|
|
@@ -44,6 +44,22 @@ const GoogleReCaptchaProvider = ({ siteKey, language, useRecaptchaNet, useEnterp
|
|
|
44
44
|
isLoading: true,
|
|
45
45
|
widgetId: siteKey
|
|
46
46
|
});
|
|
47
|
+
const successHandler = useRef(null);
|
|
48
|
+
const errorHandler = useRef(null);
|
|
49
|
+
const handleSuccess = useCallback((token) => {
|
|
50
|
+
if (successHandler.current) {
|
|
51
|
+
successHandler.current?.(token);
|
|
52
|
+
successHandler.current = null;
|
|
53
|
+
errorHandler.current = null;
|
|
54
|
+
}
|
|
55
|
+
}, []);
|
|
56
|
+
const handleError = useCallback((error) => {
|
|
57
|
+
if (errorHandler.current) {
|
|
58
|
+
errorHandler.current?.(error);
|
|
59
|
+
successHandler.current = null;
|
|
60
|
+
errorHandler.current = null;
|
|
61
|
+
}
|
|
62
|
+
}, []);
|
|
47
63
|
const onLoad = useCallback(() => {
|
|
48
64
|
const grecaptcha = window.grecaptcha?.enterprise ?? window.grecaptcha;
|
|
49
65
|
if (!grecaptcha) {
|
|
@@ -59,7 +75,9 @@ const GoogleReCaptchaProvider = ({ siteKey, language, useRecaptchaNet, useEnterp
|
|
|
59
75
|
sitekey: siteKey,
|
|
60
76
|
badge,
|
|
61
77
|
theme,
|
|
62
|
-
size: "invisible"
|
|
78
|
+
size: "invisible",
|
|
79
|
+
callback: (token) => handleSuccess(token),
|
|
80
|
+
"error-callback": (error) => handleError(error)
|
|
63
81
|
});
|
|
64
82
|
setState({
|
|
65
83
|
widgetId,
|
|
@@ -72,13 +90,24 @@ const GoogleReCaptchaProvider = ({ siteKey, language, useRecaptchaNet, useEnterp
|
|
|
72
90
|
siteKey,
|
|
73
91
|
containerId,
|
|
74
92
|
badge,
|
|
75
|
-
theme
|
|
93
|
+
theme,
|
|
94
|
+
handleSuccess,
|
|
95
|
+
handleError
|
|
76
96
|
]);
|
|
77
|
-
const execute = useCallback(
|
|
97
|
+
const execute = useCallback((action) => {
|
|
78
98
|
const grecaptcha = state?.grecaptcha;
|
|
79
|
-
if (grecaptcha?.execute)
|
|
80
|
-
|
|
81
|
-
|
|
99
|
+
if (grecaptcha?.execute) {
|
|
100
|
+
const promise = new Promise((resolve, reject) => {
|
|
101
|
+
successHandler.current = resolve;
|
|
102
|
+
errorHandler.current = reject;
|
|
103
|
+
});
|
|
104
|
+
return grecaptcha.execute(state.widgetId, action ? { action } : void 0).then((token) => {
|
|
105
|
+
if (token) handleSuccess(token);
|
|
106
|
+
return promise;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return Promise.reject("ReCaptcha is not available");
|
|
110
|
+
}, [state, handleSuccess]);
|
|
82
111
|
const reset = useCallback(() => {
|
|
83
112
|
state?.grecaptcha?.reset(state.widgetId);
|
|
84
113
|
}, [state]);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["GoogleReCaptchaContext: Context<GoogleReCaptchaContextType>","value: GoogleReCaptchaContextType"],"sources":["../src/utils.ts","../src/context.tsx","../src/hooks.ts"],"sourcesContent":["export const getRecaptchaScriptSrc = (\n render: string,\n language: string | null | undefined,\n useRecaptchaNet: boolean | undefined,\n useEnterprise: boolean | undefined,\n): string => {\n const hostName = useRecaptchaNet ? 'recaptcha.net' : 'google.com'\n const script = useEnterprise ? 'enterprise.js' : 'api.js'\n\n return `https://${hostName}/recaptcha/${script}?render=${render}${language ? `&hl=${language}` : ''}`\n}\n\nexport const injectScriptTag = (\n scriptId: string,\n language: string | null | undefined,\n useRecaptchaNet: boolean | undefined,\n useEnterprise: boolean | undefined,\n onLoad: () => void,\n): HTMLScriptElement => {\n const script = document.createElement('script')\n script.type = 'text/javascript'\n script.id = scriptId\n script.src = getRecaptchaScriptSrc('explicit', language, useRecaptchaNet, useEnterprise)\n script.async = true\n script.defer = true\n script.onload = onLoad\n\n const head = document.getElementsByTagName('head')[0]\n head.appendChild(script)\n return script\n}\n\nexport const removeScriptTag = (): void => {\n removeRecaptchBadge()\n\n const script = document.querySelector('script[src^=\"https://www.gstatic.com/recaptcha/releases\"]')\n if (script) {\n script.remove()\n }\n}\n\nexport const removeRecaptchBadge = (): void => {\n const nodeBadge = document.querySelector('.grecaptcha-badge')\n if (nodeBadge?.parentNode) {\n document.body.removeChild(nodeBadge.parentNode)\n }\n}\n","import { type Context, createContext, type JSX, useCallback, useEffect, useId, useMemo, useState } from 'react'\nimport type { GoogleReCaptchaContextType, GoogleReCaptchaProviderProps, Grecaptcha } from './types'\nimport { injectScriptTag, removeScriptTag } from './utils.ts'\n\nconst GoogleReCaptchaContext: Context<GoogleReCaptchaContextType> = createContext<GoogleReCaptchaContextType>({\n isLoading: true,\n error: null,\n execute: () => Promise.reject('useGoogleRecaptcha must be used within an GoogleReCaptchaProvider'),\n reset: () => [],\n})\n\ntype State = {\n isLoading: boolean\n error?: Error | null\n widgetId: string\n grecaptcha?: Grecaptcha | null\n}\n\nconst GoogleReCaptchaProvider = ({\n siteKey,\n language,\n useRecaptchaNet,\n useEnterprise,\n container,\n badge,\n theme,\n children,\n}: GoogleReCaptchaProviderProps): JSX.Element => {\n const id = useId()\n const containerId = useMemo(() => (container ? container : `${id}-container`), [id, container])\n const [state, setState] = useState<State>({ isLoading: true, widgetId: siteKey })\n\n // ReCaptcha初期化\n const onLoad = useCallback(() => {\n const grecaptcha = window.grecaptcha?.enterprise ?? window.grecaptcha\n if (!grecaptcha) {\n setState({\n isLoading: false,\n widgetId: siteKey,\n error: new Error('ReCaptcha is not available'),\n })\n return\n }\n grecaptcha.ready(() => {\n const widgetId = grecaptcha.render(containerId, {\n sitekey: siteKey,\n badge,\n theme,\n size: 'invisible',\n })\n setState({\n widgetId,\n grecaptcha,\n error: null,\n isLoading: false,\n })\n })\n }, [siteKey, containerId, badge, theme])\n\n const execute = useCallback(\n
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["GoogleReCaptchaContext: Context<GoogleReCaptchaContextType>","value: GoogleReCaptchaContextType"],"sources":["../src/utils.ts","../src/context.tsx","../src/hooks.ts"],"sourcesContent":["export const getRecaptchaScriptSrc = (\n render: string,\n language: string | null | undefined,\n useRecaptchaNet: boolean | undefined,\n useEnterprise: boolean | undefined,\n): string => {\n const hostName = useRecaptchaNet ? 'recaptcha.net' : 'google.com'\n const script = useEnterprise ? 'enterprise.js' : 'api.js'\n\n return `https://${hostName}/recaptcha/${script}?render=${render}${language ? `&hl=${language}` : ''}`\n}\n\nexport const injectScriptTag = (\n scriptId: string,\n language: string | null | undefined,\n useRecaptchaNet: boolean | undefined,\n useEnterprise: boolean | undefined,\n onLoad: () => void,\n): HTMLScriptElement => {\n const script = document.createElement('script')\n script.type = 'text/javascript'\n script.id = scriptId\n script.src = getRecaptchaScriptSrc('explicit', language, useRecaptchaNet, useEnterprise)\n script.async = true\n script.defer = true\n script.onload = onLoad\n\n const head = document.getElementsByTagName('head')[0]\n head.appendChild(script)\n return script\n}\n\nexport const removeScriptTag = (): void => {\n removeRecaptchBadge()\n\n const script = document.querySelector('script[src^=\"https://www.gstatic.com/recaptcha/releases\"]')\n if (script) {\n script.remove()\n }\n}\n\nexport const removeRecaptchBadge = (): void => {\n const nodeBadge = document.querySelector('.grecaptcha-badge')\n if (nodeBadge?.parentNode) {\n document.body.removeChild(nodeBadge.parentNode)\n }\n}\n","import { type Context, createContext, type JSX, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport type { GoogleReCaptchaContextType, GoogleReCaptchaProviderProps, Grecaptcha } from './types'\nimport { injectScriptTag, removeScriptTag } from './utils.ts'\n\nconst GoogleReCaptchaContext: Context<GoogleReCaptchaContextType> = createContext<GoogleReCaptchaContextType>({\n isLoading: true,\n error: null,\n execute: () => Promise.reject('useGoogleRecaptcha must be used within an GoogleReCaptchaProvider'),\n reset: () => [],\n})\n\ntype State = {\n isLoading: boolean\n error?: Error | null\n widgetId: string\n grecaptcha?: Grecaptcha | null\n}\n\nconst GoogleReCaptchaProvider = ({\n siteKey,\n language,\n useRecaptchaNet,\n useEnterprise,\n container,\n badge,\n theme,\n children,\n}: GoogleReCaptchaProviderProps): JSX.Element => {\n const id = useId()\n const containerId = useMemo(() => (container ? container : `${id}-container`), [id, container])\n const [state, setState] = useState<State>({ isLoading: true, widgetId: siteKey })\n const successHandler = useRef<(token: string) => void>(null)\n const errorHandler = useRef<(error: Error | undefined) => void>(null)\n\n const handleSuccess = useCallback((token: string) => {\n if (successHandler.current) {\n successHandler.current?.(token)\n successHandler.current = null\n errorHandler.current = null\n }\n }, [])\n\n const handleError = useCallback((error: Error | undefined) => {\n if (errorHandler.current) {\n errorHandler.current?.(error)\n successHandler.current = null\n errorHandler.current = null\n }\n }, [])\n\n // ReCaptcha初期化\n const onLoad = useCallback(() => {\n const grecaptcha = window.grecaptcha?.enterprise ?? window.grecaptcha\n if (!grecaptcha) {\n setState({\n isLoading: false,\n widgetId: siteKey,\n error: new Error('ReCaptcha is not available'),\n })\n return\n }\n grecaptcha.ready(() => {\n const widgetId = grecaptcha.render(containerId, {\n sitekey: siteKey,\n badge,\n theme,\n size: 'invisible',\n callback: (token) => handleSuccess(token),\n 'error-callback': (error) => handleError(error),\n })\n setState({\n widgetId,\n grecaptcha,\n error: null,\n isLoading: false,\n })\n })\n }, [siteKey, containerId, badge, theme, handleSuccess, handleError])\n\n const execute = useCallback(\n (action?: string) => {\n const grecaptcha = state?.grecaptcha\n if (grecaptcha?.execute) {\n const promise = new Promise<string>((resolve, reject) => {\n successHandler.current = resolve\n errorHandler.current = reject\n })\n return grecaptcha.execute(state.widgetId, action ? { action } : undefined).then((token) => {\n if (token) {\n handleSuccess(token)\n }\n // token = nullの場合、v2動作\n return promise\n })\n }\n return Promise.reject('ReCaptcha is not available')\n },\n [state, handleSuccess],\n )\n\n const reset = useCallback(() => {\n state?.grecaptcha?.reset(state.widgetId)\n }, [state])\n\n // scriptタグを追加/削除する\n useEffect(() => {\n const removeScript = injectScriptTag(id, language, useRecaptchaNet, useEnterprise, onLoad)\n return () => {\n removeScript?.remove()\n removeScriptTag()\n }\n }, [id, language, useRecaptchaNet, useEnterprise, onLoad])\n\n const value: GoogleReCaptchaContextType = useMemo(\n () => ({\n isLoading: state.isLoading,\n error: state.error ?? null,\n execute,\n reset,\n }),\n [state, execute, reset],\n )\n\n return (\n <GoogleReCaptchaContext.Provider value={value}>\n {children}\n {container ? null : <div id={`${id}-container`} />}\n </GoogleReCaptchaContext.Provider>\n )\n}\n\nexport { GoogleReCaptchaProvider, GoogleReCaptchaContext }\nexport type { GoogleReCaptchaContextType, GoogleReCaptchaProviderProps }\n","import { useContext } from 'react'\nimport { GoogleReCaptchaContext, type GoogleReCaptchaContextType } from './context'\n\nconst useGoogleRecaptcha = (): GoogleReCaptchaContextType => {\n return useContext(GoogleReCaptchaContext)\n}\n\nexport { useGoogleRecaptcha }\n"],"mappings":"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"}
|
package/dist/types.d.mts
CHANGED
|
@@ -8,6 +8,8 @@ export type Parameters = {
|
|
|
8
8
|
badge?: Badge;
|
|
9
9
|
size?: Size;
|
|
10
10
|
theme?: Theme;
|
|
11
|
+
callback?: (token: string) => void;
|
|
12
|
+
"error-callback"?: (error?: Error) => void;
|
|
11
13
|
};
|
|
12
14
|
export type GoogleReCaptchaProviderProps = PropsWithChildren<{
|
|
13
15
|
readonly siteKey: string;
|
|
@@ -24,7 +26,7 @@ export type Action = {
|
|
|
24
26
|
export type Grecaptcha = {
|
|
25
27
|
ready: (onReady: () => void) => void;
|
|
26
28
|
render: (container: string | HTMLElement, params: Parameters) => string;
|
|
27
|
-
execute: (widgetId: string, params?: Action) => Promise<string>;
|
|
29
|
+
execute: (widgetId: string, params?: Action) => Promise<string | null>;
|
|
28
30
|
reset: (widgetId: string) => void;
|
|
29
31
|
};
|
|
30
32
|
export type GoogleReCaptchaContextType = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gahojin-inc/react-google-recaptcha",
|
|
3
|
-
"version": "2025.
|
|
3
|
+
"version": "2025.9.0",
|
|
4
4
|
"description": "Hooks for Google ReCaptcha V3",
|
|
5
5
|
"author": "GAHOJIN, Inc.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
],
|
|
18
18
|
"keywords": [
|
|
19
19
|
"google-recaptcha",
|
|
20
|
+
"google-recaptcha-v2",
|
|
21
|
+
"google-recaptcha-v3",
|
|
20
22
|
"react"
|
|
21
23
|
],
|
|
22
24
|
"publishConfig": {
|