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

推荐订阅源

AWS News Blog
AWS News Blog
F
Fortinet All Blogs
T
The Blog of Author Tim Ferriss
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
A
About on SuperTechFans
WordPress大学
WordPress大学
MongoDB | Blog
MongoDB | Blog
T
Tailwind CSS Blog
美团技术团队
P
Proofpoint News Feed
MyScale Blog
MyScale Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Security Blog
Microsoft Security Blog
GbyAI
GbyAI
U
Unit 42
有赞技术团队
有赞技术团队
G
GRAHAM CLULEY
Scott Helme
Scott Helme
小众软件
小众软件
T
Threatpost
S
Securelist
SecWiki News
SecWiki News
Recent Announcements
Recent Announcements
L
LINUX DO - 最新话题
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
罗磊的独立博客
Y
Y Combinator Blog
aimingoo的专栏
aimingoo的专栏
Security Latest
Security Latest
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AI
AI
Schneier on Security
Schneier on Security
博客园 - 叶小钗
The GitHub Blog
The GitHub Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Google DeepMind News
Google DeepMind News
M
MIT News - Artificial intelligence
B
Blog RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
爱范儿
爱范儿
S
Secure Thoughts
博客园 - 聂微东
The Hacker News
The Hacker News
Stack Overflow Blog
Stack Overflow Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Cloudbric
Cloudbric
V
V2EX
Attack and Defense Labs
Attack and Defense Labs
宝玉的分享
宝玉的分享
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 时,能一次性删除当前输入框所有内容