React+Shadcn-ui实现带可删除Chip的TextArea输入功能需求
实现可转换为Chip组件的多名称输入功能
核心实现方案
- 用状态数组存储已确认的名称
- 监听文本框输入,检测到逗号时将当前输入的有效名称转为Chip组件
- 每个Chip支持单独删除,同步更新表单数据
- 提交时自动将名称数组转为逗号分隔的字符串
完整代码实现
确保已安装依赖:
npm install react-hook-form zod @hookform/resolvers @shadcn/ui
组件代码:
import { useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Textarea } from "@/components/ui/textarea"; import { Button } from "@/components/ui/button"; import { Chip } from "@/components/ui/chip"; // 表单验证规则 const formSchema = z.object({ names: z.string().optional(), }); export function NameInputForm() { const [chips, setChips] = useState<string[]>([]); const [currentInput, setCurrentInput] = useState<string>(""); const form = useForm<z.infer<typeof formSchema>>({ resolver: zodResolver(formSchema), defaultValues: { names: "", }, }); // 处理文本框输入 const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { const value = e.target.value; if (value.includes(",")) { // 拆分并过滤空名称 const newNames = value.split(",").filter(name => name.trim() !== ""); setChips(prev => [...prev, ...newNames]); setCurrentInput(""); // 同步更新表单字段 form.setValue("names", [...chips, ...newNames].join(",")); } else { setCurrentInput(value); } }; // 删除单个Chip const handleDeleteChip = (index: number) => { const updatedChips = chips.filter((_, i) => i !== index); setChips(updatedChips); form.setValue("names", updatedChips.join(",")); }; // 表单提交逻辑 const onSubmit = (data: z.infer<typeof formSchema>) => { let finalNames = chips; // 处理输入框中未确认的内容 if (currentInput.trim() !== "") { finalNames = [...finalNames, currentInput.trim()]; form.setValue("names", finalNames.join(",")); } console.log("提交的名称:", finalNames.join(",")); }; return ( <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} method="post" className="space-y-8"> <FormField control={form.control} name="names" render={({ field }) => ( <FormItem> <FormLabel>名称</FormLabel> {/* 展示已添加的Chip组件 */} <div className="flex flex-wrap gap-2 mb-2"> {chips.map((name, index) => ( <Chip key={index} onDelete={() => handleDeleteChip(index)}> {name} </Chip> ))} </div> <FormControl> <Textarea placeholder="输入名称,逗号分隔" value={currentInput} onChange={handleInputChange} {...field} /> </FormControl> <FormDescription> 输入名称后按逗号确认,每个Chip可单独删除 </FormDescription> <FormMessage /> </FormItem> )} /> <Button type="submit">提交</Button> </form> </Form> ); }
关键细节说明
- 状态同步:
chips数组存储已确认的名称,currentInput存储文本框中未确认的输入内容,两者与表单字段实时同步 - 输入处理:检测到逗号时自动拆分输入内容,过滤空值后添加到Chip列表,同时清空文本框
- 删除功能:点击Chip的删除按钮时,移除对应元素并更新表单字段值
- 提交处理:提交前检查文本框是否有未确认的内容,确保所有有效名称都被提交
内容的提问来源于stack exchange,提问作者Joseph
相关产品推荐
相关产品推荐

