50天50个小项目 (React19 + Tailwindcss V4) ✨| VerifyAccountUi(验证码组件)

📅 今天很晚才来写,我们继续 50 个小项目挑战!——VerifyAccountUi组件

仓库地址:https://gitee.com/hhm-hhm/50days50projects.git

​​​​

创建一个简洁而优雅的验证码输入界面。通过这个示例,你将学会如何构建一个响应式且用户友好的验证码输入组件,并掌握一些实用的 Vue 技巧和 Tailwind CSS 样式技巧。

准备好了吗?让我们开始吧!🚀

🌀 组件目标

  • 动态生成6个独立的验证码输入框
  • 实现自动聚焦与跳转到下一个/上一个输入框的功能
  • 添加适当的视觉反馈以提升用户体验
  • 采用 Tailwind CSS 快速构建美观的响应式布局

🔧 VerifyAccountUi.tsx组件实现

import React, { useState, useEffect, useRef } from 'react' const VerifyAccountUi: React.FC = () => { // 初始化 6 位验证码数组 const [codes, setCodes] = useState<string[]>(new Array(6).fill('')) // 创建 refs 数组,用于管理每个 input 元素 const codeInputs = useRef<(HTMLInputElement | null)[]>([]) // 组件挂载后聚焦第一个输入框 useEffect(() => { if (codeInputs.current[0]) { codeInputs.current[0].focus() } }, []) // 处理键盘事件 const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => { const key = e.key // 只允许数字、Backspace 和 Tab(Tab 用于正常跳转) if (!/^\d$/.test(key) && key !== 'Backspace' && key !== 'Tab') { e.preventDefault() return } // 数字输入 if (/^\d$/.test(key)) { const newCodes = [...codes] newCodes[index] = key setCodes(newCodes) // 自动聚焦下一个输入框 if (index < 5 && codeInputs.current[index + 1]) { setTimeout(() => codeInputs.current[index + 1]?.focus(), 10) } } // 退格键处理 if (key === 'Backspace') { const newCodes = [...codes] newCodes[index] = '' setCodes(newCodes) // 聚焦上一个输入框(如果当前为空或刚被清空) if (index > 0 && codeInputs.current[index - 1]) { setTimeout(() => codeInputs.current[index - 1]?.focus(), 10) } e.preventDefault() // 阻止默认删除行为(避免重复触发) } } // 同步更新输入框值(受控组件) const handleChange = (e: React.ChangeEvent<HTMLInputElement>, index: number) => { const value = e.target.value // 只保留最后一位数字(防止粘贴多字符) if (/^\d?$/.test(value)) { const newCodes = [...codes] newCodes[index] = value.slice(-1) setCodes(newCodes) } } return ( <div className="flex min-h-screen items-center justify-center overflow-hidden bg-gray-900 font-sans"> <div className="max-w-2xl rounded-lg border-4 border-black bg-white p-8 text-center"> <h2 className="mb-2 text-2xl font-bold">Verify Your Account</h2> <p className="mb-8 text-gray-600"> We emailed you the six digit code to cool_guy@email.com <br /> Enter the code below to confirm your email address. </p> {/* 验证码输入框容器 */} <div className="mb-8 flex flex-wrap items-center justify-center"> {codes.map((_, index) => ( <input key={index} ref={(el) => { codeInputs.current[index] = el }} value={codes[index]} onChange={(e) => handleChange(e, index)} onKeyDown={(e) => handleKeyDown(e, index)} className="mx-1 my-2 h-20 w-20 rounded-md border border-gray-200 text-center text-5xl font-light [caret-color:transparent] transition-all duration-300 focus:border-blue-500 focus:shadow-lg focus:shadow-blue-200/50 focus:outline-none" type="text" maxLength={1} inputMode="numeric" pattern="[0-9]" aria-label={`Digit ${index + 1}`} /> ))} </div> <small className="inline-block max-w-md rounded-md bg-gray-100 px-4 py-2 text-sm text-gray-600"> This is design only. We didn't actually send you an email as we don't have your email, right? </small> </div> <div className="fixed right-20 bottom-5 z-100 text-2xl text-red-500"> CSDN@Hao_Harrision </div> </div> ) } export default VerifyAccountUi

🔄 关键差异总结

功能Vue 3React + TS
响应式数组ref(['', '', ...])useState<string[]>(...)
多元素引用ref([])+v-for refuseRef+ 回调 ref
输入绑定v-modelvalue + onChange + onKeyDown
生命周期onMounteduseEffect(..., [])
类型安全string[],HTMLInputElement

🔁 转换说明

1.状态管理:ref([])useState<string[]>([])

VueReact
const codes = ref(new Array(6).fill(''))const [codes, setCodes] = useState<string[]>(new Array(6).fill(''))

✅ 使用 TypeScript 泛型确保类型安全。


2.DOM 引用:ref([])useRef<(HTMLInputElement | null)[]>

Vue 中:

const codeInputs = ref([]) // v-for 中绑定 ref="codeInputs"

React 中需手动管理 ref 数组:

const codeInputs = useRef<(HTMLInputElement | null)[]>([]) // 在 render 中: <input ref={(el) => { codeInputs.current[index] = el }} />

✅ 这是 React 中批量管理多个 DOM 引用的标准做法。


3.生命周期:onMounteduseEffect(..., [])

useEffect(() => { codeInputs.current[0]?.focus(); }, []);

✅ 确保只在挂载时聚焦第一个输入框。


4.事件处理:@keydownonKeyDown+onChange

为什么需要onChange
  • Vue 的v-model自动处理输入;
  • React 是受控组件,必须通过value + onChange同步状态;
  • 仅靠onKeyDown无法处理粘贴、拖拽、中文输入法等场景。

因此新增handleChange

const handleChange = (e, index) => { const value = e.target.value; if (/^\d?$/.test(value)) { // 只接受 0 或 1 位数字 setCodes([...codes.slice(0, index), value.slice(-1), ...codes.slice(index + 1)]); } };

✅ 提升健壮性,防止用户粘贴 "123" 到单个输入框。


5.键盘事件逻辑迁移

  • 保留数字输入自动跳转;
  • 保留 Backspace 回退;
  • 阻止非数字输入(如字母、符号);
  • 使用setTimeout(..., 10)确保 DOM 更新后再聚焦(与 Vue 行为一致)。

6.无障碍与语义化

添加aria-label提升可访问性:

aria-label={`Digit ${index + 1}`}

7.样式完全复用

  • 所有 Tailwind 类名直接迁移;
  • [caret-color:transparent]隐藏光标(设计需求);
  • inputMode="numeric"触发移动端数字键盘。

✅ 注意事项

  1. 不要省略onChange:否则粘贴内容会失效;
  2. 使用回调 ref:避免useRef数组在 re-render 时丢失引用;
  3. 正则校验/^\d$/e.key >= 0更可靠(避免特殊键干扰);
  4. 防抖聚焦setTimeout确保 React 完成状态更新后再操作 DOM。

💡 可选优化

  • 将完整验证码拼接为字符串:const fullCode = codes.join('')
  • 添加“重新发送”按钮;
  • 支持鼠标点击聚焦任意输入框。

🎨 TailwindCSS 样式重点讲解

🎯 TailwindCSS 样式说明
类名作用
min-h-screen设置最小高度为视口高度,确保页面至少占满整个屏幕的高度
items-center/justify-center分别在交叉轴(垂直)和主轴(水平)上居中对齐子元素
overflow-hidden隐藏超出容器的内容,防止溢出导致滚动条出现
bg-gray-50设置背景颜色为浅灰色,作为整体页面背景色
font-sans使用无衬线字体系列
max-w-2xl设置最大宽度为 2xl (72rem),限制主要内容区域的最大宽度
rounded-lg给元素添加较大的圆角半径
border-4 border-black添加黑色边框,边框宽度为 4px
bg-white设置背景颜色为白色,用于主要内容区域的背景色
p-8设置内边距为 8 个单位,提供足够的空间来分隔内容与边界
text-center文本居中对齐
mb-2/mb-8设置下外边距分别为 2 和 8 个单位,用于控制元素间的间距
text-2xl/text-sm设置文本大小为 2xl (1.5rem) 和 sm (0.875rem)
font-bold设置字体加粗
text-gray-600设置文本颜色为灰色,提供对比度但不过于强烈
flex-wrap允许子元素换行排列,适应不同屏幕尺寸
mx-1 my-2分别设置左右外边距为 1 单位和上下外边距为 2 单位,用于输入框之间的间距
h-20 w-20设置固定的高度和宽度为 20 个单位,定义输入框的大小
rounded-md给元素添加中等大小的圆角半径
border border-gray-200添加浅灰色边框,增加视觉层次感
text-center文本居中对齐,适用于单字符输入框的设计
text-5xl font-light设置大号轻量级字体,使验证码数字更加突出
[caret-color:transparent]将光标颜色设置为透明,隐藏默认的闪烁光标
transition-all duration-300应用所有属性的变化过渡效果,持续时间为 300ms
focus:border-blue-500 focus:shadow-lg focus:shadow-blue-200/50当输入框获得焦点时,改变边框颜色、添加阴影以增强视觉反馈
focus:outline-none移除默认的聚焦轮廓线,避免干扰设计的一致性

🦌 路由组件 + 常量定义

router/index.tsxchildren数组中添加子路由

{ path: '/', element: <App />, children: [ ... { path: '/VerifyAccountUi', lazy: () => import('@/projects/VerifyAccountUi').then((mod) => ({ Component: mod.default, })), }, ], },
constants/index.tsx 添加组件预览常量
import demo41Img from '@/assets/pic-demo/demo-41.png' 省略部分.... export const projectList: ProjectItem[] = [ 省略部分.... { id: 41, title: 'Verify Account Ui', image: demo41Img, link: 'VerifyAccountUi', }, ]

🚀 小结

通过这篇文章,我们使用 React19 和 TailwindCSS 创建一个直观且易于使用的验证码输入界面。通过合理利用 react的useState和 Tailwind CSS 的强大样式能力,我们可以轻松地构建出既美观又功能强大的前端组件。

想要让你的验证码输入界面更加完善?考虑以下扩展功能:

✅ 表单提交验证:增加对用户输入验证码的验证逻辑。
✅ 错误提示:当验证码无效时显示相应的错误消息。
✅ 倒计时重发机制:模拟验证码过期后重新发送验证码的功能。

📅 明日预告: 我们将完成RangeSlider组件,一个可以自定义进度的范围滑块组件。🚀

感谢阅读,欢迎点赞、收藏和分享 😊


原文链接:https://blog.csdn.net/qq_44808710/article/details/149778543https://blog.csdn.net/qq_44808710/article/details/149779252https://blog.csdn.net/qq_44808710/article/details/149778543

每天造一个轮子,码力暴涨不是梦!🚀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1147185.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BetterJoy控制器终极配置指南:从新手到专家完全攻略

BetterJoy控制器终极配置指南&#xff1a;从新手到专家完全攻略 【免费下载链接】BetterJoy Allows the Nintendo Switch Pro Controller, Joycons and SNES controller to be used with CEMU, Citra, Dolphin, Yuzu and as generic XInput 项目地址: https://gitcode.com/gh…

在线电路仿真在低噪声放大器设计的应用

在线电路仿真如何重塑低噪声放大器设计&#xff1f;你有没有遇到过这样的情况&#xff1a;花了几周时间画好一个LNA&#xff08;低噪声放大器&#xff09;原理图&#xff0c;制板回来却发现增益不够、噪声偏高&#xff0c;甚至自激振荡&#xff1f;更糟的是&#xff0c;你还不确…

ncmdump解密神器:解锁网易云音乐NCM格式的终极方案

ncmdump解密神器&#xff1a;解锁网易云音乐NCM格式的终极方案 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM加密文件无法在其他设备播放而困扰吗&#xff1f;&#x1f3b5; 今天我要为你介绍一款专业解…

5大核心功能揭秘:yfinance如何成为金融数据获取的终极利器

5大核心功能揭秘&#xff1a;yfinance如何成为金融数据获取的终极利器 【免费下载链接】yfinance Download market data from Yahoo! Finances API 项目地址: https://gitcode.com/GitHub_Trending/yf/yfinance 在当今数据驱动的金融世界中&#xff0c;快速准确地获取市…

如何用英雄联盟智能工具彻底改变你的游戏体验

如何用英雄联盟智能工具彻底改变你的游戏体验 【免费下载链接】LeagueAkari ✨兴趣使然的&#xff0c;功能全面的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/LeagueAkari 还在为选英雄时的犹豫不决而烦…

NS-USBLoader终极指南:Switch文件管理的全能解决方案

NS-USBLoader终极指南&#xff1a;Switch文件管理的全能解决方案 【免费下载链接】ns-usbloader Awoo Installer and GoldLeaf uploader of the NSPs (and other files), RCM payload injector, application for split/merge files. 项目地址: https://gitcode.com/gh_mirror…

纪念币预约新革命:智能工具如何让你告别手忙脚乱

纪念币预约新革命&#xff1a;智能工具如何让你告别手忙脚乱 【免费下载链接】auto_commemorative_coin_booking 项目地址: https://gitcode.com/gh_mirrors/au/auto_commemorative_coin_booking 还在为每次纪念币发行时的激烈竞争而焦虑吗&#xff1f;那些反复刷新页面…

网页视频无法下载?这5个智能资源嗅探技巧帮你一键搞定

网页视频无法下载&#xff1f;这5个智能资源嗅探技巧帮你一键搞定 【免费下载链接】cat-catch 猫抓 chrome资源嗅探扩展 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 还在为网页视频无法保存而烦恼吗&#xff1f;每次看到精彩的短视频、在线课程或有价…

SmartDock桌面启动器:让Android设备秒变专业工作站的5个核心步骤

SmartDock桌面启动器&#xff1a;让Android设备秒变专业工作站的5个核心步骤 【免费下载链接】smartdock A user-friendly desktop mode launcher that offers a modern and customizable user interface 项目地址: https://gitcode.com/gh_mirrors/smar/smartdock 想要…

如何快速绕过付费墙:5个终极免费内容解锁技巧

如何快速绕过付费墙&#xff1a;5个终极免费内容解锁技巧 【免费下载链接】bypass-paywalls-chrome-clean 项目地址: https://gitcode.com/GitHub_Trending/by/bypass-paywalls-chrome-clean 还在为看到精彩文章却被付费墙挡住而烦恼吗&#xff1f;作为普通用户&#x…

如何轻松突破付费墙限制:Bypass Paywalls Clean 完整使用教程

如何轻松突破付费墙限制&#xff1a;Bypass Paywalls Clean 完整使用教程 【免费下载链接】bypass-paywalls-chrome-clean 项目地址: https://gitcode.com/GitHub_Trending/by/bypass-paywalls-chrome-clean 在当今信息爆炸的时代&#xff0c;优质内容往往被付费墙所阻…

ResNet18快速部署:Docker容器一键启动

ResNet18快速部署&#xff1a;Docker容器一键启动 1. 引言 1.1 通用物体识别的现实需求 在智能监控、内容审核、图像检索和辅助决策等场景中&#xff0c;通用物体识别是计算机视觉的基础能力。传统方案依赖云API调用&#xff0c;存在网络延迟、权限限制、服务不稳定等问题。…

DownKyi哔哩下载姬:终极免费B站视频下载神器

DownKyi哔哩下载姬&#xff1a;终极免费B站视频下载神器 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xff09;。 …

League Akari:英雄联盟智能助手全方位使用指南

League Akari&#xff1a;英雄联盟智能助手全方位使用指南 【免费下载链接】LeagueAkari ✨兴趣使然的&#xff0c;功能全面的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/LeagueAkari 还在为选英雄时手…

ResNet18优化技巧:模型量化压缩实战指南

ResNet18优化技巧&#xff1a;模型量化压缩实战指南 1. 引言&#xff1a;通用物体识别中的ResNet-18价值与挑战 在当前AI应用广泛落地的背景下&#xff0c;通用图像分类已成为智能设备、内容审核、辅助驾驶等场景的基础能力。其中&#xff0c;ResNet-18作为深度残差网络中最轻…

5个实用技巧帮你彻底掌握B站视频下载神器

5个实用技巧帮你彻底掌握B站视频下载神器 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xff09;。 项目地址: ht…

MusicFree插件系统终极指南:轻松打造全能音乐播放器

MusicFree插件系统终极指南&#xff1a;轻松打造全能音乐播放器 【免费下载链接】MusicFreePlugins MusicFree播放插件 项目地址: https://gitcode.com/gh_mirrors/mu/MusicFreePlugins 想要一个播放器就能听遍全网音乐吗&#xff1f;MusicFree插件系统让这个梦想成真&a…

智能内容解锁工具:免费阅读付费内容的终极方案

智能内容解锁工具&#xff1a;免费阅读付费内容的终极方案 【免费下载链接】bypass-paywalls-chrome-clean 项目地址: https://gitcode.com/GitHub_Trending/by/bypass-paywalls-chrome-clean 还在为优质内容被付费墙阻挡而苦恼吗&#xff1f;今天带你深入了解智能内容…

HBuilderX新手入门配置:零基础手把手教程

HBuilderX 新手入门指南&#xff1a;从零开始的高效开发之旅 你是不是刚接触前端开发&#xff0c;面对五花八门的编辑器无从下手&#xff1f; 有没有试过 VS Code 配一堆插件还跑不起来项目&#xff1f; 或者想做小程序&#xff0c;却被复杂的环境配置劝退&#xff1f; 别急…

快速理解Multisim元件库下载路径配置核心要点

搞定Multisim元件库下载与路径配置&#xff1a;从“找不到元件”到团队高效协同的实战指南 你有没有遇到过这种情况&#xff1f; 千辛万苦从TI官网或论坛下载了一个新型电机驱动芯片的Multisim模型&#xff0c;解压后放进某个文件夹&#xff0c;兴冲冲打开软件想用它搭个H桥电…