Reputation: 499
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
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