惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
V
Vulnerabilities – Threatpost
C
CERT Recently Published Vulnerability Notes
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
Y
Y Combinator Blog
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
MongoDB | Blog
MongoDB | Blog
MyScale Blog
MyScale Blog
Security Latest
Security Latest
T
Threat Research - Cisco Blogs
量子位
I
Intezer
Simon Willison's Weblog
Simon Willison's Weblog
C
Cybersecurity and Infrastructure Security Agency CISA
L
Lohrmann on Cybersecurity
L
LINUX DO - 最新话题
The Register - Security
The Register - Security
T
Tailwind CSS Blog
爱范儿
爱范儿
Google DeepMind News
Google DeepMind News
T
Troy Hunt's Blog
Stack Overflow Blog
Stack Overflow Blog
Cloudbric
Cloudbric
S
Secure Thoughts
The GitHub Blog
The GitHub Blog
T
The Blog of Author Tim Ferriss
L
LangChain Blog
Recorded Future
Recorded Future
小众软件
小众软件
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Tor Project blog
人人都是产品经理
人人都是产品经理
F
Full Disclosure
O
OpenAI News
Webroot Blog
Webroot Blog
A
Arctic Wolf
TaoSecurity Blog
TaoSecurity Blog
P
Privacy & Cybersecurity Law Blog
Jina AI
Jina AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
雷峰网
雷峰网
Microsoft Security Blog
Microsoft Security Blog
H
Heimdal Security Blog
B
Blog RSS Feed
Vercel News
Vercel News

博客园 - 冯叶青

OpenAI vs Anthropic API 对比:响应体、请求体、消息格式与工具调用 GitHub Actions 自动部署流程 Git分支自动合并脚本:基于时间戳的冲突解决方案 Next.js lingui.js 多语言自动提取翻译键 - ats-node Next.js 中优雅地使用 Lottie 动画 Next.js 路由参数更新最佳实践:从 replaceState 到 nextReplaceState Jenkins构建时SWR模块导入报错解决方案 解决 Jenkins 环境下 Lingui 构建报错 "btoa is not defined" git 提交 实现大图自动压缩功能 React lingui.js 多语言自动提取翻译键 - ast node html2canvas 解决截图空白问题 react 实现前端发版监测 JS根据文件名获取文件类型 JS获取本机IP地址 适用于react、vue菜单格式化工具函数 git 内容提交 实现大图拦截功能 JS实现视频截图 next.js 利用中间件(middleware.ts)实现PC与移动路由无缝切换 Android生成签名文件及对apk进行签名 JS-SDK 配置,实现微信分享功能
React实现短信验证码输入组件
冯叶青 · 2025-03-10 · via 博客园 - 冯叶青

在日常开发中,短信验证码输入是一个非常常见的需求。今天和大家分享一个基于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 时,能一次性删除当前输入框所有内容