React OTP验证组件报错:Cannot read properties of undefined (reading 'value')
错误原因与修复方案
核心错误:事件参数顺序搞反了
你在绑定handleInput时用了handleInput.bind(null, index),这会把index作为第一个参数传给handleInput,导致函数里的e实际上是数字类型的index,自然没有target.value属性,直接触发了Cannot read properties of undefined (reading 'value')错误。
其他问题与完整修复代码
另外你的代码还存在计数失效、事件无法解绑、缺少焦点切换逻辑的问题,以下是修复后的完整代码:
import { useState, useEffect, useRef } from 'react' import '../src/component.css' export default function OTPComponent() { const [count, setCount] = useState(0); const inpRef = useRef([]); useEffect(() => { // 修复计数:用函数式更新获取最新count值,改用setInterval实现持续计数 const timer = setInterval(() => { setCount(prev => prev + 1); }, 1000); const handleInput = (e, index) => { const current = inpRef.current[index]; const next = inpRef.current[index + 1]; const value = e.target.value; // 只允许输入单个数字 const isValid = /^\d?$/.test(value); if (!isValid) { e.target.value = ""; return; } // 输入有效数字后,自动跳转到下一个输入框 if (value && next) { next.focus(); } }; const handleKeyDown = (e, index) => { const prev = inpRef.current[index - 1]; // 退格键且当前输入框为空时,跳转到上一个输入框 if (e.key === 'Backspace' && !e.target.value && prev) { prev.focus(); } }; // 绑定事件:用箭头函数传递正确参数,避免bind生成新函数导致无法解绑 inpRef.current.forEach((input, index) => { if (input) { const inputHandler = (e) => handleInput(e, index); const keyDownHandler = (e) => handleKeyDown(e, index); input.addEventListener('input', inputHandler); input.addEventListener('keydown', keyDownHandler); // 存储绑定的函数,方便后续移除 input._handlers = { inputHandler, keyDownHandler }; } }); return () => { clearInterval(timer); // 移除事件:用之前存储的函数确保匹配 inpRef.current.forEach((input) => { if (input && input._handlers) { input.removeEventListener('input', input._handlers.inputHandler); input.removeEventListener('keydown', input._handlers.keyDownHandler); delete input._handlers; } }); }; }, []); // 组件挂载后自动聚焦第一个输入框 useEffect(() => { if (inpRef.current[0]) { inpRef.current[0].focus(); } }, []); return ( <> <p> Counter : {count}</p> <h4>Now enter the OTP</h4> <div className="whole"> <h5>Send the OTP to your phone Number</h5> <div className="container"> {[...Array(6)].map((_, index) => ( <input className="text-field" type="number" ref={(ref) => { inpRef.current[index] = ref; }} key={index} maxLength={1} /> ))} </div> <button className="btn">SUBMIT</button> </div> </> ); }
关键修改点说明
- 修正事件参数传递:用箭头函数
(e) => handleInput(e, index)传递事件对象和索引,确保handleInput里的e是正确的事件对象。 - 修复计数逻辑:把单次执行的
setTimeout改成setInterval实现持续计数,并用setCount(prev => prev + 1)的函数式更新,确保每次获取最新的count值。 - 添加焦点切换逻辑:
input事件:输入有效数字后,自动聚焦下一个输入框。keydown事件:按下退格键且当前输入框为空时,聚焦上一个输入框。
- 正确解绑事件:把绑定的事件函数存储在input元素的
_handlers属性里,移除时直接调用存储的函数,避免bind生成新函数导致无法解绑。 - 自动聚焦第一个输入框:新增useEffect,组件挂载后自动聚焦第一个OTP输入框,提升用户体验。
- 限制输入长度:给input添加
maxLength={1},确保每个输入框只能输入一个字符。
内容的提问来源于stack exchange,提问作者web-innov-design
相关产品推荐
相关产品推荐

