React父组件如何调用子组件resetToken方法重置验证码
父组件调用子组件内部方法实现方案
React 组件内部的函数默认是闭包隔离的,不能直接跨组件导入调用,要实现这个需求最直接的方式是通过 forwardRef + useImperativeHandle 将子组件的内部方法暴露给父组件,具体修改步骤如下:
- 首先修正原有代码的潜在问题:你当前的
resetToken每次执行都会新建定时器但从未清理,多次触发后会出现倒计时跳变、内存泄漏问题,同时JSX中的原生class属性需要改为React要求的className,父组件里的事件要从原生onclick改为驼峰写法onClick。
修改Captcha验证码组件
- 从React引入
forwardRef、useImperativeHandle、useRef相关方法,用forwardRef包裹组件接收父组件传入的ref - 用ref存储定时器ID,每次执行
resetToken前先清理旧定时器,避免重复创建 - 通过
useImperativeHandle将resetToken方法绑定到对外暴露的ref上 - 组件卸载时清理所有定时器,防止内存泄漏
修改后的完整Captcha.jsx代码:
import React, { forwardRef, useImperativeHandle, useRef, useEffect, useState } from 'react'; import './Captcha.css'; const Captcha = forwardRef(({ statusOfCaptcha }, ref) => { const [status, setStatus] = useState(undefined); const [code, setCode] = useState(undefined); const [text, setText] = useState(""); const [seconds, setSeconds] = useState(120); // 存储定时器ID,用于清理 const autoRefreshTimer = useRef(null); const countdownTimer = useRef(null); const resetToken = () => { // 先清理已有定时器,避免重复创建 if (autoRefreshTimer.current) clearInterval(autoRefreshTimer.current); if (countdownTimer.current) clearInterval(countdownTimer.current); // 生成6位随机验证码 const chars = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz123456789"; let newCode = ""; for (let i = 0; i < 6; i++) { newCode += chars.charAt(Math.floor(Math.random() * chars.length)); } setCode(newCode); // 重置输入状态 setText(""); setStatus(undefined); setSeconds(120); // 120秒自动刷新验证码 autoRefreshTimer.current = setInterval(() => { let refreshCode = ""; for (let i = 0; i < 6; i++) { refreshCode += chars.charAt(Math.floor(Math.random() * chars.length)); } setCode(refreshCode); setSeconds(120); setStatus(undefined); setText(""); }, 120000); // 每秒更新倒计时 countdownTimer.current = setInterval(() => { setSeconds(prev => prev - 1); }, 1000); } // 对外暴露resetToken方法供父组件调用 useImperativeHandle(ref, () => ({ resetToken })) useEffect(() => { resetToken(); // 组件卸载时清理所有定时器 return () => { if (autoRefreshTimer.current) clearInterval(autoRefreshTimer.current); if (countdownTimer.current) clearInterval(countdownTimer.current); } }, []); const checkCaptcha = (inputVal) => { if (inputVal === code) { setStatus(true); statusOfCaptcha(true); } else { setStatus(false); statusOfCaptcha(false); } } return ( <div className='captcha'> <div className="background"> <p onCopy={(e) => e.preventDefault()} className="unselectable">{code}</p> <a>{seconds}</a> </div> <div className='input-captcha'> <input type="text" placeholder="Zadejte kód" value={text} onChange={(e) => { checkCaptcha(e.target.value); setText(e.target.value) }} /> {status === false && (<i className='bx bx-x text-color-red'></i>)} {status === true && (<i className='bx bx-check text-color-green'></i>)} </div> </div> ) }) export default Captcha;
修改父页面index.jsx
- 引入
useRef创建ref实例,绑定到Captcha组件上 - 补全原代码缺失的验证码校验状态定义
- 点击重置按钮时,通过
captchaRef.current.resetToken()直接调用子组件方法 - 修正原代码的事件写法错误
修改后的父组件代码:
import React, { useRef, useState } from 'react' import Captcha from '../../../components/Captcha/Captcha'; function Index() { // 创建ref对接子组件暴露的方法 const captchaRef = useRef(null); // 存储验证码校验结果状态 const [isCaptchaValid, setIsCaptchaValid] = useState(false); const handleReset = () => { // 调用子组件暴露的重置方法,可选链判断避免ref未挂载时报错 captchaRef.current?.resetToken(); } return ( <div> <Captcha ref={captchaRef} statusOfCaptcha={setIsCaptchaValid}/> <button onClick={handleReset}>Reset captcha code</button> </div> ) } export default Index
替代方案(无ref实现)
如果不想使用ref,也可以通过状态监听的方式实现:在父组件维护一个resetTrigger的数字状态,点击按钮时给这个值+1,将该值作为prop传给Captcha组件,子组件内用useEffect监听这个prop的变化,触发时执行resetToken即可。这种方式适合逻辑简单的场景,但相比ref暴露方法的方式会多维护一个冗余状态,按需选择即可。
内容的提问来源于stack exchange,提问作者user19364233
相关产品推荐
相关产品推荐

