You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 02:47:02