Loading

React实现短信验证码输入组件

在日常开发中,短信验证码输入是一个非常常见的需求。今天和大家分享一个基于React实现的验证码输入组件,它具有以下特点:

  • 支持6位数字验证码输入
  • 自动聚焦下一个输入框
  • 支持回退删除
  • 支持移动端输入
  • 输入完成后自动触发回调

组件代码实现

const SmsVerify = ({
  onComplete,
  onChange
}: {
  onComplete?: (code: string) => void
  onChange?: (value: string) => void
}) => {
  const [code, setCode, getCode] = useGetState<string[]>([])
  const inputRefs = useRef<(HTMLInputElement | null)[]>([])

  useEffect(() => {
    const seat = new Array(6).fill('')
    setCode(seat)
  }, [])

  useEffect(() => {
    // 当所有格子都填满时触发完成回调
    if (code.every((v) => v !== '') && onComplete) {
      onComplete(code.join(''))
    }
    onChange?.(code.join(''))
  }, [code, onComplete])

  const handleOnChange = ({
    value,
    index
  }: {
    value: string | number
    index: number
  }) => {
    // 将value转换为字符串并确保只保留数字
    const stringValue = String(value).replace(/[^0-9]/g, '')
    // 只取第一个数字
    const singleDigit = stringValue.slice(0, 1)

    if (singleDigit) {
      setCode(
        produce((draft) => {
          draft[index] = singleDigit
        })
      )
      // 自动聚焦下一个输入框
      if (index < 5) {
        inputRefs.current[index + 1]?.focus()
      }
    }
  }

  const handleKeyDown = (
    e: React.KeyboardEvent<HTMLInputElement>,
    index: number
  ) => {
    if (e.key === 'Backspace') {
      // 当前格子为空时,删除键将焦点移到上一个输入框
      setCode(
        produce((draft) => {
          draft[index] = ''
        })
      )
      inputRefs.current[index - 1]?.focus()
      e.preventDefault()
    }
  }

  return (
    <div className="sms-verify">
      <div className="flex gap-[0.1rem]">
        {code.map((value, index) => {
          return (
            <InputNumber
              className={classNames('text-[0.4rem]', { 'has-content': value })}
              ref={(el) => {
                if (el) {
                  inputRefs.current[index] = el
                }
              }}
              type="tel"
              value={value}
              key={'T-' + index}
              controls={false}
              onKeyDown={(e) => handleKeyDown(e, index)}
              onInput={(value) =>
                handleOnChange({ value: value || '', index })
              }
            />
          )
        })}
      </div>
    </div>
  )
}

  

关键点

  • 使用 type="tel" 调起数字键盘,如果把type类型换成number,在移动端会有兼容性问题,用户可以输入特殊字符和中文,用type="tel",则不会有这个情况
  • onInput 代替onChange事件,如果用onChange,最后一个输入框可以无限制输入内容,删除内容时,无法删除输入的全部内容,而使用onInput 时,能一次性删除当前输入框所有内容
posted @ 2025-03-10 15:17  冯叶青  阅读(156)  评论(0)    收藏  举报