'Firebase RecaptchaVerifier.clear() has no effect
I have react web app where I want to implement phone auth. I have initialized recaptchaVerifier based on docs and examples. However if I want to submit the form again (say because of the error). I get error: Error: reCAPTCHA has already been rendered in this element
.
I have tried to remove the verifier using .clear
method, but that seems to have no effect. Bellow is example code.
Is there something I have overlooked?
Thank you.
class PhoneAuth extends React.Component {
static contextTypes = {
firebase: PropTypes.object.isRequired,
};
state = {
phone: '',
}
onChangeHandler = (e) => {
this.setState({
[e.target.name]: e.target.value,
})
}
onPhoneLoginSubmit = (e) => {
const { onVerificationSend } = this.props
const {phone} = this.state
const {firebase }=this.context
e.preventDefault()
this.applicationVerifier = new firebase.auth.RecaptchaVerifier(
'recaptcha-container',
{ size: 'invisible' }
);
firebase.auth().signInWithPhoneNumber(phone, this.applicationVerifier)
.then((result) => {
onVerificationSend(result.verificationId)
this.applicationVerifier.clear()
})
.catch (error => {
this.setState({formError: error})
this.applicationVerifier.clear()
})
}
render() {
const { intl } = this.props;
const { phone, formError } = this.state
return (
<div>
<form onSubmit={this.onPhoneLoginSubmit} name="phone-signin" method="post">
<input
id="phone"
name="phone"
type="phone"
onChange={this.onChangeHandler}
value={phone}
/>
<label className="input__label" htmlFor="email">
phone number
</label>
{formError && (
<p >
{formError.code}
</p>
)}
<button
type="submit"
>
sign in
</button>
</form>
<div id="recaptcha-container"></div>
</div>
)
}
}
Solution 1:[1]
So, I have figure it out. The problem wasn't clear() method - that is working properly. But I have to recreate the captcha container from scratch myself. Doc isn't clear about it, so that's confused me. In the docs there is:
Clears the reCAPTCHA widget from the page and destroys the current instance.
So I thought that it will be removed via the method.
So, right now I have this code in the render method:
<div ref={ref => this.recaptchaWrapperRef = ref}>
<div id="recaptcha-container"></div>
</div>
and then in the submit callback:
if (this.applicationVerifier && this.recaptchaWrapperRef) {
this.applicationVerifier.clear()
this.recaptchaWrapperRef.innerHTML = `<div id="recaptcha-container"></div>`
}
// Initialize new reCaptcha verifier
this.applicationVerifier = new firebase.auth.RecaptchaVerifier(
'recaptcha-container',
{ size: 'invisible' }
);
This is the only way how I manage to do it in react. If someone still ahve better way to do it, feel free to post it. As I think this is pretty ugly (for React).
Solution 2:[2]
use captchaVarifier outside to your onPhoneLoginSubmit()
method
do not use
this.applicationVerifier
this.applicationVerifier = new firebase.auth.RecaptchaVerifier(
'recaptcha-container',
{ size: 'invisible' }
);
instead use window.recaptchaVerifier and remove .clear()
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
"recaptcha-container",
{
size: "invisible"
}
);
onPhoneLoginSubmit = (e) => {
firebase.auth().signInWithPhoneNumber(phone, window.recaptchaVerifier)
.then((result) => {
onVerificationSend(result.verificationId)
})
.catch (error => {
this.setState({formError: error})
})
}
then use setTimeout()
function with a delay of 1 or 2 second, if you do not use setTimeout()
the recaptcha container <div id="recaptcha-container"></div>
will execute after this code which will produce an error
so use setTimeout()
like, example given below
componentWillMount(){
setTimeout(() => {
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
"recaptcha-container",
{
size: "invisible"
}
);
}, 2000);
}
onPhoneLoginSubmit = (e) => {
firebase.auth().signInWithPhoneNumber(phone, window.recaptchaVerifier)
.then((result) => {
onVerificationSend(result.verificationId)
})
.catch (error => {
this.setState({formError: error})
})
}
Solution 3:[3]
I have got the same issue with React. but there is a problem of Ref. so I have created a new instance using Id of a parent div element
<div ref={ref => this.recaptchaWrapperRef = ref}>
<div id="recaptcha-container"></div>
</div>
In the submit callback add this.
document.getElementById("recaptcha-container").innerHTML = "<div id='recaptcha'></div>";
Solution 4:[4]
Use this code if you're using Firebase v9.
if (this.window.recaptchaVerifier) {
this.window.recaptchaVerifier.recaptcha.reset(
this.window.recaptchaWidgetId
);
} else {
this.window.recaptchaVerifier = new RecaptchaVerifier(
'sign-in',
{
size: 'invisible',
callback: () => {
// sign in with phone number
},
},
getAuth()
);
this.window.recaptchaWidgetId =
await this.window.recaptchaVerifier.render();
}
Solution 5:[5]
Just use window.recaptchaVerifier.reset() inside catch eg
const signInWithPhoneNumber = (fullMobile) => {
return new Promise((resolve, reject) => {
auth.signInWithPhoneNumber(fullMobile, window.recaptchaVerifier).then(res => {
resolve(res)
})
.catch(error => {
console.log(error)
window.recaptchaVerifier.reset()
reject(error)
})
})
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Tomáš Pustelník |
Solution 2 | Anil Kumar |
Solution 3 | VB MAVANI |
Solution 4 | Charlie Levine |
Solution 5 | Enzo Aliatis |