React JS中如何校验随机生成的移动端OTP完成手机号验证
手机号验证功能实现落地方案
组件流程串联逻辑
采用父组件做流程状态机的模式,避免跨组件状态耦合:
- 顶层父组件维护两个核心状态:
currentStep(可选值mobileInput/otpInput)、boundMobile(存储校验通过的有效手机号) - MobileNo组件提交校验通过后,触发父组件回调:将
currentStep切换为otpInput,把当前提交的有效手机号写入boundMobile状态 - MobileOtp组件挂载时,从父组件读取
boundMobile作为校验请求的必填参数,不额外在组件内重复存手机号
该模式直接解决状态赋值不匹配问题:所有跨组件共享状态全部上提,两个子组件仅维护自身的输入值、错误提示、按钮loading状态
补全字段校验逻辑
MobileNo组件规则
- 输入阶段自动过滤非数字字符,限制最大输入长度10位
- 提交前强制校验:手机号长度严格等于10位、首位不为0
- 校验不通过时在输入框下方展示明确错误提示,提交按钮置灰不触发请求
核心实现代码:
const [mobile, setMobile] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const handleMobileInput = (e) => { // 过滤非数字,截取前10位 const inputValue = e.target.value.replace(/\D/g, '').slice(0, 10); setMobile(inputValue); // 输入时自动清除错误提示 if (error) setError(''); }; const handleSubmitMobile = async () => { if (mobile.length !== 10) { setError('请输入10位有效手机号'); return; } if (mobile[0] === '0') { setError('手机号不能以0开头'); return; } setLoading(true); try { // 本地测试可使用Math.floor(1000 + Math.random() * 9000)生成OTP const testOtp = Math.floor(1000 + Math.random() * 9000); // 调用后端接口将OTP与手机号绑定存储 await saveOtpToBackend(mobile, testOtp); // 触发父组件切换到OTP输入步骤 onMobileVerified(mobile); } catch (err) { setError('提交失败,请稍后重试'); } finally { setLoading(false); } };
MobileOtp组件规则
- 输入阶段自动过滤非数字字符,限制最大输入长度4位
- 提交前强制校验:OTP长度严格等于4位
- 内置60秒重发倒计时,倒计时未结束时禁用重发按钮
核心实现代码:
const [otp, setOtp] = useState(''); const [error, setError] = useState(''); const [countdown, setCountdown] = useState(60); const [loading, setLoading] = useState(false); // 倒计时逻辑 useEffect(() => { if (countdown <= 0) return; const timer = setTimeout(() => setCountdown(prev => prev - 1), 1000); return () => clearTimeout(timer); }, [countdown]); const handleOtpInput = (e) => { const inputValue = e.target.value.replace(/\D/g, '').slice(0,4); setOtp(inputValue); if (error) setError(''); }; const handleSubmitOtp = async () => { if (otp.length !== 4) { setError('请输入4位有效OTP'); return; } setLoading(true); try { // 调用后端接口比对OTP const isValid = await validateOtpFromBackend(boundMobile, otp); if (!isValid) { setError('OTP错误,请重新输入'); return; } // 校验通过,触发后续业务逻辑 onOtpVerified(); } catch (err) { setError('校验失败,请稍后重试'); } finally { setLoading(false); } };
OTP比对校验逻辑
- 后端存储规则:OTP生成后与对应手机号绑定,设置5分钟有效期,同一手机号重复提交OTP请求时,直接覆盖旧的OTP记录
- 校验流程:
- MobileOtp组件提交时,将用户输入的OTP与绑定的手机号一起传给后端校验接口
- 后端首先查询存储中对应手机号的OTP记录:若记录不存在、已过有效期,直接返回校验失败
- 若记录有效,比对用户传入的OTP与存储的OTP值:完全一致则返回校验通过,同时删除该OTP记录防止重复使用;不一致直接返回失败
生产环境禁止在前端生成、返回OTP明文,所有OTP的生成、存储、比对逻辑全部放在后端实现,避免校验失效。
现有问题修复点
- 状态不匹配:移除两个子组件之间的状态依赖,所有共享状态全部上提至父组件,保证状态变更时同步更新
- 校验缺失:输入阶段实时过滤非法字符,提交前做格式、长度校验,输入时自动清除历史错误提示
- 体验优化:提交时增加loading状态,接口失败时给出明确重试提示,避免用户重复点击提交
内容的提问来源于stack exchange,提问作者Ashish Maidankar
相关产品推荐
相关产品推荐

