📅 我们继续 50 个小项目挑战!——
RandomChoicePicker组件
仓库地址:https://gitee.com/hhm-hhm/50days50projects.git
构建一个简单的标签输入组件。用户可以在文本框中输入多个选项,并通过逗号分隔,组件会自动将其拆分成可视化的“标签”展示出来。
🌀 组件目标
- 接收用户输入的一段文本。
- 使用逗号
,分割输入内容。 - 动态渲染为一组“标签”(Tag)。
- 使用 TailwindCSS 快速构建美观现代的 UI 界面。
- 提供清晰的交互反馈。
🔧RandomChoicePicker.tsx 组件实现
import React, { useState, useEffect } from 'react' const RandomChoicePicker: React.FC = () => { const [textareaText, setTextareaText] = useState<string>('') const [tagList, setTagList] = useState<string[]>([]) // 每当 textareaText 变化时,自动分割标签 useEffect(() => { const tags = textareaText .split(',') .map((item) => item.trim()) // 去除前后空格 .filter((item) => item !== '') // 过滤空字符串 setTagList(tags) }, [textareaText]) return ( <div className="flex h-screen items-center justify-center"> <div className="w-full max-w-2xl rounded-2xl bg-gray-400 p-8 shadow-lg"> <h3 className="font-mono text-2xl text-gray-800"> Enter all of the choices divided by a comma (',').(输入所有选项,并用英文逗号,分隔) <br /> Press enter when you're done </h3> <textarea className="my-4 h-36 w-full resize-none rounded-lg bg-gray-200 p-4 text-gray-800 placeholder-gray-500 focus:ring-2 focus:ring-blue-300 focus:outline-none" placeholder="Enter choices here..." value={textareaText} onChange={(e) => setTextareaText(e.target.value)} /> {tagList.length > 0 && ( <div className="mt-4 flex flex-wrap gap-2"> {tagList.map((item, index) => ( <div key={`${item}-${index}`} // 使用 index 避免重复 key(因 item 可能重复) className="rounded-2xl bg-amber-200 px-3 py-1 text-sm font-medium text-gray-800"> {item} </div> ))} </div> )} </div> <div className="absolute right-20 bottom-10 text-red-500">CSDN@Hao_Harrision</div> </div> ) } export default RandomChoicePicker✅ 关键实现说明
| 功能 | Vue 实现 | React + TS 实现 |
|---|---|---|
| 双向绑定文本域 | v-model="textareaText" | value + onChange控制 |
| 自动分割逗号内容 | watchEffect+splitTag() | useEffect监听textareaText |
| 标签渲染 | v-for="item in tagList" | {tagList.map(...)} |
| 空值过滤 | 无(原逻辑会保留空字符串) | ✅ 添加.trim()和filter(item !== '')提升体验 |
🛠️ 改进细节
去重与清理
- 使用
.trim()去除每个选项前后的空格(如" apple "→"apple")。 - 过滤掉空字符串,避免显示空白标签。
- 使用
Key 策略
- 因用户可能输入重复项(如
"A, A, B"),不能仅用item作 key。 - 改为
key={item−{index}}确保唯一性,避免 React 警告。
- 因用户可能输入重复项(如
UI/UX 增强
- 添加
resize-none禁止手动调整 textarea 大小(保持布局稳定)。 - 添加
focus:ring提升交互反馈。 - 使用
flex-wrap确保标签在小屏换行。 - 添加
bg-gray-100背景色提升整体可读性。
- 添加
类型安全
textareaText: stringtagList: string[]
📌 注意事项
- 此组件目前只负责输入和解析,不包含“随机选择”逻辑(如抽一个标签)。如果你后续需要“Pick Random”功能,可以在此基础上加一个按钮调用:
const pickRandom = () => { if (tagList.length > 0) { const random = tagList[Math.floor(Math.random() * tagList.length)]; alert(`Selected: ${random}`); } };
🎨 TailwindCSS 样式重点讲解
| 类名 | 作用 |
|---|---|
flex,items-center,justify-center | 居中布局整个容器 |
h-screen | 容器高度为视口全高 |
rounded-2xl | 圆角大小为 1rem |
bg-gray-400、bg-gray-200、bg-amber-200 | 设置背景颜色 |
p-8,p-4,p-1 | 不同层级的内边距 |
my-4 | 上下外边距为 1rem |
w-full | 宽度为 100% |
h-36 | 高度为 9rem |
text-2xl | 字体大小为 1.5rem |
font-mono | 使用等宽字体 |
gap-2 | flex 子元素之间间隔为 0.5rem |
h-8 | 高度为 2rem |
rounded-2xl | 圆角为 1rem |
这些类名帮助我们快速构建出一个居中的响应式布局,并确保视觉上的一致性和美观性。
🦌 路由组件 + 常量定义
router/index.tsx中children数组中添加子路由
{ path: '/', element: <App />, children: [ ... { path: '/RandomChoicePicker', lazy: () =>import('@/projects/RandomChoicePicker.tsx').then((mod) => ({ Component: mod.default, })), }, ], },constants/index.tsx 添加组件预览常量
import demo13Imgfrom '@/assets/pic-demo/demo-13.png' 省略部分.... export const projectList: ProjectItem[] = [ 省略部分.... { id: 13, title: 'Random Choice Picker', image: demo13Img, link: 'RandomChoicePicker', }, ]🚀 小结
作为表单组件的一部分,用于收集用户输入的多项数据。
📅 明日预告: 我们将完成AnimatedNavigation组件,一个非常有意思的动画的导航组件!🚀
原文链接:https://blog.csdn.net/qq_44808710/article/details/148615314
每天造一个轮子,码力暴涨不是梦!🚀