React集成Firebase发送OTP时出现TypeError的问题排查与解决
我在React项目中用Firebase实现手机号OTP验证,手机号从其他组件传入。点击发送OTP时出现错误:TypeError: Cannot read properties of undefined (reading 'verify')。相关代码如下:
OtpVerification.js
import React, { useState } from 'react'; import { auth, } from '../firebase'; import { signInWithPhoneNumber } from 'firebase/auth'; const OtpVerification = ({ phoneNumber }) => { const [otp, setOtp] = useState(''); const [verificationId, setVerificationId] = useState(null); const onSendOtp = () => { // Ensure that the phone number is valid before sending OTP if (!phoneNumber) { console.error('Invalid phone number'); return; } const appVerifier = window.recaptchaVerifier; // Send OTP to the provided phone number signInWithPhoneNumber(auth, `+${phoneNumber}`, appVerifier) .then((confirmationResult) => { // Save the verification ID to use later for OTP confirmation // setVerificationId(confirmationResult.verificationId); window.confirmationResult = confirmationResult; console.log('OTP sent successfully'); // Additional logic if needed }) .catch((error) => { console.error('Error sending OTP:', error); // Handle error appropriately }); }; const onVerify = () => { if (!verificationId || !otp) { console.error('Invalid verification ID or OTP'); return; } // Confirm the OTP using the verification ID signInWithPhoneNumber(auth, verificationId, otp) .then((result) => { console.log('Phone number verified successfully:', result); // Additional logic if needed }) .catch((error) => { console.error('Error verifying OTP:', error); // Handle error appropriately }); }; return ( <div className='otpVerification'> <input type='number' value={otp} onChange={(e) => setOtp(e.target.value)} maxLength={6} /> <button onClick={onSendOtp}>Send OTP</button> <button onClick={onVerify}>Verify</button> </div> ); }; export default OtpVerification;
firebase.js
import { initializeApp } from "firebase/app"; import {getDatabase} from 'firebase/database'; import { getAuth } from "firebase/auth"; const firebaseConfig = { apiKey: "", authDomain: "", projectId: "", storageBucket: "", messagingSenderId: "", appId: "" }; const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getDatabase(app); export {auth,db};
问题:
- 该错误的成因是什么?
- 如何解决?
- 还有其他更简便的React OTP验证实现方式吗?
错误成因
这个报错的核心原因是**window.recaptchaVerifier未被正确初始化**。Firebase手机号验证强制要求搭配reCAPTCHA验证防止机器人请求,你直接从window对象获取它,但实际没有提前创建并渲染reCAPTCHA实例,导致appVerifier为undefined,最终调用signInWithPhoneNumber时内部尝试访问它的verify方法触发报错。
另外onVerify方法存在API误用:验证OTP应该调用confirmationResult.confirm(otp),而非再次调用signInWithPhoneNumber——后者是用来发起发送OTP请求的,不是验证OTP的接口。
解决方法
1. 正确初始化reCAPTCHA
在组件加载时创建reCAPTCHA实例,确保DOM中存在对应的渲染容器:
import React, { useState, useEffect } from 'react'; import { auth, } from '../firebase'; import { signInWithPhoneNumber } from 'firebase/auth'; const OtpVerification = ({ phoneNumber }) => { const [otp, setOtp] = useState(''); const [confirmationResult, setConfirmationResult] = useState(null); useEffect(() => { // 初始化reCAPTCHA实例 window.recaptchaVerifier = new auth.RecaptchaVerifier('recaptcha-container', { 'size': 'invisible', // 可选:invisible/normal/compact 'callback': () => { // reCAPTCHA验证成功回调,可留空 } }, auth); }, []); const onSendOtp = () => { if (!phoneNumber) { console.error('无效的手机号'); return; } const appVerifier = window.recaptchaVerifier; signInWithPhoneNumber(auth, `+${phoneNumber}`, appVerifier) .then((result) => { setConfirmationResult(result); // 保存验证结果实例,无需存到window console.log('OTP已发送'); }) .catch((error) => { console.error('发送OTP失败:', error); // 重置reCAPTCHA实例避免重复报错 window.recaptchaVerifier.reset(); }); }; const onVerify = () => { if (!confirmationResult || !otp) { console.error('验证实例或OTP无效'); return; } // 用正确的API验证OTP confirmationResult.confirm(otp) .then((result) => { console.log('手机号验证成功:', result.user); // 此处可获取验证后的用户对象,处理后续逻辑 }) .catch((error) => { console.error('验证OTP失败:', error); window.recaptchaVerifier.reset(); }); }; return ( <div className='otpVerification'> {/* 必须添加reCAPTCHA渲染容器 */} <div id="recaptcha-container"></div> <input type='number' value={otp} onChange={(e) => setOtp(e.target.value)} maxLength={6} /> <button onClick={onSendOtp}>发送OTP</button> <button onClick={onVerify}>验证</button> </div> ); }; export default OtpVerification;
2. 确保手机号格式正确
传入的phoneNumber需包含正确国家码前缀(或在代码中拼接正确的国家码),比如8613xxxxxxxxx,避免格式错误导致请求失败。
更简便的React OTP验证实现方式
1. 使用Firebase UI Auth
Firebase官方提供的UI组件,一键集成手机号验证,无需手动处理reCAPTCHA和OTP输入逻辑:
import React from 'react'; import { auth } from '../firebase'; import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth'; const OtpVerification = () => { const uiConfig = { signInFlow: 'popup', signInOptions: [ { provider: auth.PhoneAuthProvider.PROVIDER_ID, recaptchaParameters: { size: 'invisible' } } ], callbacks: { signInSuccessWithAuthResult: () => false // 阻止自动跳转,自行处理后续逻辑 } }; return ( <StyledFirebaseAuth uiConfig={uiConfig} firebaseAuth={auth} /> ); }; export default OtpVerification;
需先安装依赖:npm install react-firebaseui firebase
2. 搭配第三方OTP输入组件
如果不想用Firebase UI,可使用react-otp-input优化OTP输入体验,同时保留自定义Firebase验证逻辑:
安装依赖:npm install react-otp-input
使用示例:
import OtpInput from 'react-otp-input'; // 替换原input组件 <OtpInput value={otp} onChange={setOtp} numInputs={6} renderSeparator={<span>-</span>} renderInput={(props) => <input {...props} />} />
内容的提问来源于stack exchange,提问作者Balram Thakur

