'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