让控件跟着组件挂载
cap 使用浏览器控件,不依赖 React 专用 SDK。组件显示时创建容器,脚本加载后调用 window.capCaptcha.mount();组件移除时清理事件和容器。
下面的组件使用普通表单提交。把它保存为 CapForm.jsx,在已有 React 项目中传入你的公开 sitekey。后端沿用表单文章中的 /contact 处理逻辑。
CapForm.jsx下载文件 ↓
import { useEffect, useRef, useState } from 'react';
let loader;
function widgetReady() {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error('widget unavailable')), 10000);
customElements.whenDefined('altcha-widget').then(() => { clearTimeout(timer); resolve(); });
});
}
function loadCap() {
if (window.capCaptcha) return widgetReady();
if (!loader) loader = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cap.bug.im/v1.js';
script.onload = () => {
if (!window.capCaptcha) { reject(new Error('cap unavailable')); return; }
widgetReady().then(resolve, reject);
};
script.onerror = () => { script.remove(); loader = undefined; reject(new Error('cap unavailable')); };
document.head.append(script);
});
return loader;
}
export default function CapForm({ sitekey }) {
const host = useRef(null);
const [status, setStatus] = useState('正在加载验证控件…');
useEffect(() => {
let cancelled = false;
const node = host.current;
const container = document.createElement('div');
container.className = 'cap-captcha';
container.dataset.sitekey = sitekey;
const onState = event => {
const state = event.detail?.state;
setStatus(state === 'verified' ? '验证完成,可以提交。' : state === 'error' ? '验证失败,请重试。' : '请完成验证。');
};
let widget;
node.append(container);
loadCap().then(() => {
if (!cancelled) {
window.capCaptcha.mount();
widget = container.querySelector('altcha-widget');
widget?.addEventListener('statechange', onState);
setStatus('请完成验证。');
}
}).catch(() => { if (!cancelled) setStatus('控件加载失败,请刷新后重试。'); });
return () => {
cancelled = true;
widget?.removeEventListener('statechange', onState);
container.remove();
};
}, [sitekey]);
return <form method="post" action="/contact">
<label>留言 <textarea name="message" maxLength={2000} required /></label>
<div ref={host} />
<p role="status">{status}</p>
<button type="submit">提交</button>
</form>;
}
在页面中使用
import CapForm from './CapForm.jsx';
export default function Contact() {
return <CapForm sitekey="YOUR_SITEKEY" />;
}路由切换与开发模式
React 开发模式可能重复执行 Effect 的设置与清理。示例每次创建独立容器,清理时移除,避免重复挂载。如果使用支持服务端组件的框架,请将该文件标为客户端组件,再在客户端执行控件逻辑。
脚本载入成功不代表发题成功。域名、网络或 sitekey 配置问题会在控件中显示错误。脚本加载失败则刷新页面重试。
如果改用 fetch 提交
在 submit 处理器中用 new FormData(event.currentTarget) 读取隐藏字段,并发送给你自己的后端。不要从浏览器直接携带 secret 调用验证接口。成功提交、错误重试或重新显示表单时,重新挂载控件取得新 token。
生命周期依据:React useEffect 文档。