You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 19:40:21