Reputation: 33
I have a ready-made form in React
I'm trying to add a captcha to it but it would seem that with the only correct option the captcha reload infinity loops
I didt think that in such a simple task in React there could be so many problems
import { GoogleReCaptchaProvider, GoogleReCaptcha } from 'react-google-recaptcha-v3'
type Props = {
onSubmit: (values: AuthRequest) => Promise<AuthResponse>
}
function AuthForm(props: Props) {
const [token, setToken] = useState('')
return (
<div className={cn('container')}>
<GoogleReCaptchaProvider reCaptchaKey="[key]">
<Form
onSubmit={handlers.submit}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<FormField name={'email'} />
<div>
<FormField name={'password'} />
</div>
<GoogleReCaptcha
onVerify={(token) => {
setToken(token)
}}
/>
<div>
<Button type="submit">Submit</Button>
</div>
</form>
)}
/>
</GoogleReCaptchaProvider>
</div>
)
}
export { AuthForm }
Upvotes: 2
Views: 7875
Reputation: 11
I solved it by using the function component and use memo and sending the form data to the provider as chide props.
import React from 'react';
import {GoogleReCaptcha, GoogleReCaptchaProvider} from "react-google-recaptcha-v3";
const GetTokenGoogleReCaptcha : FC = (props) => {
const {sendToken, refresh} = props;
const [token, setToken] = React.useState("");
const [refreshReCaptcha, setRefreshReCaptcha] = React.useState(false);
React.useEffect(() => {
sendToken(token)
}, [token])
React.useEffect(() => {
if(refresh){
refreshToken();
}
}, [refresh])
const getTokenFunction = React.useCallback((token) => {
setToken(token);
}, []);
const refreshToken = () => {
setRefreshReCaptcha(r => !r);
}
return (
<GoogleReCaptchaProvider reCaptchaKey={"reCaptchaKey"}>
<GoogleReCaptcha
onVerify={getTokenFunction}
refreshReCaptcha={refreshReCaptcha}
/>
</GoogleReCaptchaProvider>
);
};
export default React.memo(GetTokenGoogleReCaptcha);
then in form have:
import React from 'react';
import {StringUtilities} from "../../utilities/Utilities";
import {ProfileFormItems} from "../../enums/profileEnum";
import CustomTextInput from "../../components/generalComponents/CustomTextInput";
import GetTokenGoogleReCaptcha from "../../components/customComponents/GetTokenGoogleReCaptcha";
const SignIn = () => {
const [token, setToken] = React.useState("");
const [RefreshToken, setRefreshToken] = React.useState(false);
const handleSubmit = async (e) => {
console.log('token', token)
setRefreshToken(true);
//submit formData
};
return (<div className={"sing-in-wrap card"}>
<div className={"card-header"}>
<div className={"title"}>
<h2>
signIn
</h2>
</div>
</div>
<form autoComplete="off" onSubmit={handleSubmit}>
<div className={"card-body"}>
<div className={"row"}>
<div className={"col-12"}>
<div className={"form-group"}>
<Input
id={ProfileFormItems.email}
name={ProfileFormItems.email}
value={signInObject.email}
onChange={handleChange(ProfileFormItems.email)}
label={ProfileFormItems.email}
/>
</div>
</div>
<div className={"col-12"}>
<div className={"form-group "}>
<Input
id={ProfileFormItems.password}
name={ProfileFormItems.password}
value={signInObject.password}
onChange={handleChange(ProfileFormItems.password)}
label={ProfileFormItems.password}
/>
</div>
</div>
</div>
</div>
<div className={"card-footer"}>
<div className={'form-group d-grid'}>
<button type="submit" className={"btn btn-primary"}>
signIn"
</button>
</div>
</div>
</form>
<GetTokenGoogleReCaptcha
sendToken={(token)=> setToken(token)}
refresh={RefreshToken}
/>
</div>);
};
export default React.memo(SignIn);
Upvotes: 0
Reputation: 803
You don't state it but I assume you are using react-google-recaptcha-v3
?
According to the documentation over at https://www.npmjs.com/package/react-google-recaptcha-v3
// IMPORTANT NOTES: The
GoogleReCaptcha
component is a wrapper arounduseGoogleRecaptcha
hook and useuseEffect
to run the verification. It's important that you understand how React hooks work to use it properly. Avoid using inline function for theonVerify
props as it can possibly cause the verify function to run continously. To avoid that problem, you can use a memoized function provided byReact.useCallback
or a class method
I think the loop happens because the GoogleReCaptcha
is causing itself to be rerendered by calling a callback that modifies the state of the parent element constantly. BTW, I tried wrapping GoogleReCaptcha
in React.memo()
, that didn't work (anyway, that's just optimization, not guaranteed to work, so a poor choice to protect unecessary API calls).
Since I am using class based components, none of the solutions to this type of problem really appealed to me. In the end, I solved this by simply not rendering GoogleReCaptcha
after the first callback, something like this:
constructor(props) {
super(props);
this.state = {
...
reCaptchaToken: null
}
}
...
<GoogleReCaptchaProvider reCaptchaKey="[key]">
...
{ this.state.reCaptchaToken===null ?
<GoogleReCaptcha
onVerify={(reCaptchaToken) => {
this.setState({reCaptchaToken:reCaptchaToken})
}}
/>
:
''
}
...
</GoogleReCaptchaProvider>
Upvotes: 0
Reputation: 39
I solved it like this
import { GoogleReCaptchaProvider, GoogleReCaptcha } from 'react-google-recaptcha-v3'
type Props = {
onSubmit: (values: AuthRequest) => Promise<AuthResponse>
}
function AuthForm(props: Props) {
const [token, setToken] = useState('')
const verifyRecaptchaCallback = React.useCallback((token) => {
setToken(token)
}, []);
return (
<div className={cn('container')}>
<GoogleReCaptchaProvider reCaptchaKey="[key]">
<Form
onSubmit={handlers.submit}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<FormField name={'email'} />
<div>
<FormField name={'password'} />
</div>
<GoogleReCaptcha
onVerify={verifyRecaptchaCallback}
/>
<div>
<Button type="submit">Submit</Button>
</div>
</form>
)}
/>
</GoogleReCaptchaProvider>
</div>
)
}
export { AuthForm }
Upvotes: 3