React路由跳转时Google reCAPTCHA不加载且表单无法提交求助
React Router导航时Google reCAPTCHA加载失败问题
我在表单中集成了Google reCAPTCHA,页面刷新或首次访问组件时功能正常,但通过React Router从其他组件导航到表单组件时,reCAPTCHA无法加载,表单也无法提交。已尝试关闭React严格模式、在useEffect中调用recaptchaRef.current.reset(),均无效。控制台打印recaptchaRef显示对象已加载,相关代码如下:
import React, { useState, useRef } from 'react'; import { Link } from 'react-router-dom'; import ReCAPTCHA from 'react-google-recaptcha'; export const ContactUs = (props) => { const host = process.env.REACT_APP_SERVER_HOST; const port = process.env.REACT_APP_SERVER_PORT; const { showAlert } = props; const recaptchaRef = useRef(); const [formData, setFormData] = useState({ email: '', password: '' }); const handleOnChange = (e) => {setFormData({ ...formData, [e.target.name]: e.target.value })}; const handleSubmit = async (e) => { e.preventDefault(); const recaptchaToken = await recaptchaRef.current.executeAsync(); // Recaptcha token recaptchaRef.current.reset(); // Reset recaptcha to make it ready for another check const response = await fetch(`http://${host}:${port}/api/contacts/addcontact`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({...formData, recaptchaToken}) }); const jsonResponse = await response.json(); if (jsonResponse.success) { document.querySelectorAll('input, textarea').forEach((element) => {element.value=''}); showAlert('Contact Saved Successfully, I\'ll come back to you soon', 'success'); } else { let errorMsg = "" jsonResponse.errors.map(error => errorMsg += error.msg + "\n"); showAlert(errorMsg, 'error'); } } return ( <form onSubmit={handleSubmit}> ... <ReCAPTCHA sitekey={process.env.REACT_APP_RECAPTCHA_SITE_KEY} size="invisible" ref={recaptchaRef} /> <div className="inputBox"> <button type="submit" className='btn submit-btn'>Submit</button> </div> </form> ) }
解决方案
问题核心是React Router的单页导航不会重新加载全局reCAPTCHA脚本,而react-google-recaptcha组件在路由切换后的挂载过程中,可能无法正确初始化reCAPTCHA实例。以下是针对性解决方法:
1. 动态控制reCAPTCHA脚本的加载与清理
通过useEffect监听组件生命周期,手动注入/移除reCAPTCHA脚本,确保每次组件挂载时脚本都能正确初始化:
import React, { useState, useRef, useEffect } from 'react'; import { Link } from 'react-router-dom'; import ReCAPTCHA from 'react-google-recaptcha'; export const ContactUs = (props) => { const host = process.env.REACT_APP_SERVER_HOST; const port = process.env.REACT_APP_SERVER_PORT; const { showAlert } = props; const recaptchaRef = useRef(); const [recaptchaLoaded, setRecaptchaLoaded] = useState(false); const [formData, setFormData] = useState({ email: '', password: '' }); const handleOnChange = (e) => { setFormData({ ...formData, [e.target.name]: e.target.value }); }; useEffect(() => { let script = document.getElementById('recaptcha-script'); // 检查脚本是否已存在,不存在则动态注入 if (!script) { script = document.createElement('script'); script.id = 'recaptcha-script'; script.src = `https://www.google.com/recaptcha/api.js?render=${process.env.REACT_APP_RECAPTCHA_SITE_KEY}`; script.async = true; script.defer = true; script.onload = () => { setRecaptchaLoaded(true); // 脚本加载完成后重置reCAPTCHA实例 recaptchaRef.current?.reset(); }; document.body.appendChild(script); } else { setRecaptchaLoaded(true); recaptchaRef.current?.reset(); } // 组件卸载时移除脚本,避免路由切换时的冲突 return () => { if (script) { document.body.removeChild(script); setRecaptchaLoaded(false); } }; }, []); const handleSubmit = async (e) => { e.preventDefault(); // 校验reCAPTCHA加载状态 if (!recaptchaLoaded || !recaptchaRef.current) { showAlert('reCAPTCHA加载失败,请稍后重试', 'error'); return; } try { const recaptchaToken = await recaptchaRef.current.executeAsync(); recaptchaRef.current.reset(); const response = await fetch(`http://${host}:${port}/api/contacts/addcontact`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({...formData, recaptchaToken}) }); const jsonResponse = await response.json(); if (jsonResponse.success) { document.querySelectorAll('input, textarea').forEach((element) => {element.value=''}); showAlert('联系方式已保存,我会尽快回复你', 'success'); } else { let errorMsg = ""; jsonResponse.errors.map(error => errorMsg += error.msg + "\n"); showAlert(errorMsg, 'error'); } } catch (err) { showAlert('提交失败,请检查网络或reCAPTCHA状态', 'error'); console.error(err); } } return ( <form onSubmit={handleSubmit}> {/* 表单其他输入元素 */} {/* 仅在reCAPTCHA加载完成后渲染组件 */} {recaptchaLoaded && ( <ReCAPTCHA sitekey={process.env.REACT_APP_RECAPTCHA_SITE_KEY} size="invisible" ref={recaptchaRef} /> )} <div className="inputBox"> {/* 加载完成前禁用提交按钮 */} <button type="submit" className='btn submit-btn' disabled={!recaptchaLoaded}>提交</button> </div> </form> ) }
2. 可选方案:切换到reCAPTCHA v3
如果业务允许,推荐使用react-google-recaptcha-v3库,它基于无交互的v3版本,无需渲染可视化组件,更适配单页应用的路由场景,避免实例初始化问题。
内容的提问来源于stack exchange,提问作者Ayush
相关产品推荐
相关产品推荐

