Ali Bhutta
Ali Bhutta

Reputation: 499

Error: reCAPTCHA has already been rendered in this element, when sending OTP in ReactJs via Firebase

I'm using Firebase to send OTP on user mobile number, I'm implementing it into ReactJS. If first time I send OTP by clicking Button, it works fine, but if I click button more than 1 times without refreshing the page I get error "reCAPTCHA has already been rendered in this element".

I'm not able to find proper solution for this. I tried many other solutions, found by googling but no one worked for me. Your help/suggestions will be helpful for me. Thank you.

Firebase Code for sending OTP:-

var recaptchaVerifier = new RecaptchaVerifier(
      "recaptcha-container",
      {
        size: "invisible",
      },
      auth
    );

    recaptchaVerifier.render();
    signInWithPhoneNumber(auth, mobileNumber, recaptchaVerifier)
      .then((confirmationResult) => {
        setFirebaseOtpResult(confirmationResult);
        setShowModal(true);
      })
      .catch((error) => {
        addToast("Something went wrong, please try again", {
          appearance: "error",
          autoDismiss: true,
        });
        console.log("error", error);
      });
  };

Component Code:-

import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { useSelector } from "react-redux";
import PhoneInput from "react-phone-number-input";
import "react-phone-number-input/style.css";
import { auth } from "../utils/firebase";
import { RecaptchaVerifier, signInWithPhoneNumber } from "firebase/auth";
import { Modal } from "react-bootstrap";
import "./forgot_password.style.css";
import { useToasts } from "react-toast-notifications";
import { API } from "../utils/api";

const ForgotPassword = () => {
  const history = useHistory();
  const { addToast } = useToasts();
  const { setting } = useSelector((state) => state.layoutSetting);
  const style = {
    color: setting.web_font_color,
    fontVariant: setting.web_font_variant,
    fontStyle: setting.web_font_style,
    fontWeight: setting.web_font_weight,
  };
  const phoneNumberValue = "";
  const [otp, setOtp] = useState("");
  const [mobileNumber, setMobileNumber] = useState("");
  const [showModal, setShowModal] = useState(false);
  const [firebaseOtpResult, setFirebaseOtpResult] = useState("");
  const [userId, setUserId] = useState("");

  const submitHandler = async (e) => {
    e.preventDefault();

    const params = { mobilenumber: mobileNumber };
    const config = {
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    };
    const { data } = await API.post("/foroget_passworod", params, config);
    if (data.code !== "SUCCESS") {
      addToast(data.message, {
        appearance: "error",
        autoDismiss: true,
      });
      return;
    }

    var recaptchaVerifier = new RecaptchaVerifier(
      "recaptcha-container",
      {
        size: "invisible",
      },
      auth
    );

    recaptchaVerifier.render();
    signInWithPhoneNumber(auth, mobileNumber, recaptchaVerifier)
      .then((confirmationResult) => {
        setFirebaseOtpResult(confirmationResult);
        setShowModal(true);
      })
      .catch((error) => {
        addToast("Something went wrong, please try again", {
          appearance: "error",
          autoDismiss: true,
        });
        console.log("error", error);
      });
    setUserId(data.data.id);
  };

  const submitHandlerOTP = (e) => {
    e.preventDefault();
    if (!otp.trim().length) {
      addToast("Please enter OTP code", {
        appearance: "error",
        autoDismiss: true,
      });
      return;
    }
    firebaseOtpResult
      .confirm(otp)
      .then((result) => {
        addToast("OTP verified", {
          appearance: "success",
          autoDismiss: true,
        });
        localStorage.setItem("user_id", JSON.stringify(userId));
        history.push("/change-password");
      })
      .catch((error) => {
        addToast("Invalid OTP enterd", {
          appearance: "error",
          autoDismiss: true,
        });
      });
  };

  return (
    <>
      <div className="login-form-container">
        <div className="login-register-form">
          <form onSubmit={submitHandler}>
            <PhoneInput
              placeholder="Mobile Number"
              international={true}
              defaultCountry="PK"
              countryCallingCodeEditable={false}
              value={phoneNumberValue}
              required
              onChange={setMobileNumber}
            />
            <div id="recaptcha-container"></div>
            <div className="button-box">
              <button
                type="submit"
                style={{ ...style, background: setting.web_background_color }}
              >
                <span>Send OTP</span>
              </button>
            </div>
          </form>
        </div>
      </div>

      {/* OTP Modal */}
      <Modal show={showModal}>
        <Modal.Header> Verify OTP </Modal.Header>
        <Modal.Body>
          <div className="row">
            <div className="col-md-12">
              <div className="login-form-container">
                <div className="login-register-form">
                  <form onSubmit={submitHandlerOTP}>
                    <input
                      type="number"
                      placeholder="Please enter OPT sent to you"
                      required
                      onChange={(e) => setOtp(e.target.value)}
                    />
                    <div id="recaptcha-container"></div>
                    <div className="button-box" style={{ textAlign: "center" }}>
                      <button
                        type="submit"
                        style={{
                          ...style,
                          background: setting.web_background_color,
                          border: "none",
                          padding: "7px 30px",
                          marginTop: "25px",
                        }}
                      >
                        <span>Verify</span>
                      </button>
                      <button
                        type="button"
                        style={{
                          ...style,
                          background: setting.web_background_color,
                          border: "none",
                          padding: "7px 30px",
                          marginTop: "25px",
                          marginLeft: "10px",
                        }}
                        onClick={() => setShowModal(false)}
                      >
                        <span>Close</span>
                      </button>
                    </div>
                  </form>
                </div>
              </div>
            </div>
          </div>
        </Modal.Body>
      </Modal>
    </>
  );
};

export default ForgotPassword;

firebase.js

import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const config = {
    apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
    authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
    projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
    storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
    appId: process.env.REACT_APP_FIREBASE_APP_ID
};

const app = initializeApp(config);
export const auth = getAuth(app); 
export default app;

Upvotes: 2

Views: 4493

Answers (1)

Ali Bhutta
Ali Bhutta

Reputation: 499

Issue resolved When I changed firebase code

from:-

var recaptchaVerifier = new RecaptchaVerifier(
   "recaptcha-container",
    {
     size: "invisible",
    },
      auth
);
recaptchaVerifier.render();

To:-

if(!window.recaptchaVerifier){
      window.recaptchaVerifier = new RecaptchaVerifier(
        "recaptcha-container",
        {
          size: "invisible",
        },
        auth
      );
 }
window.recaptchaVerifier.render();

We don't need to create object of RecaptchaVerifier when an object already exists.

I hope this may help future visitors,

Upvotes: 1

Related Questions