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

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>
    </>
  );
}

关键修改点说明

  1. 修正事件参数传递:用箭头函数(e) => handleInput(e, index)传递事件对象和索引,确保handleInput里的e是正确的事件对象。
  2. 修复计数逻辑:把单次执行的setTimeout改成setInterval实现持续计数,并用setCount(prev => prev + 1)的函数式更新,确保每次获取最新的count值。
  3. 添加焦点切换逻辑:
    • input事件:输入有效数字后,自动聚焦下一个输入框。
    • keydown事件:按下退格键且当前输入框为空时,聚焦上一个输入框。
  4. 正确解绑事件:把绑定的事件函数存储在input元素的_handlers属性里,移除时直接调用存储的函数,避免bind生成新函数导致无法解绑。
  5. 自动聚焦第一个输入框:新增useEffect,组件挂载后自动聚焦第一个OTP输入框,提升用户体验。
  6. 限制输入长度:给input添加maxLength={1},确保每个输入框只能输入一个字符。

内容的提问来源于stack exchange,提问作者web-innov-design

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 01:40:01