React动态Checklist组件中的输入框状态重置与空格字符处理问题
React动态Checklist组件中的输入框状态重置与空格字符处理问题
我正在开发一个React组件来管理准备清单,用户可以添加、删除和保存项目。大部分功能都正常工作,但遇到两个问题:
输入框状态重置问题
当添加新的清单项目时,输入字段(newField)有时会意外重置,这可能是由于组件重新渲染导致的。当我更新items状态时,整个组件会重新渲染,从而引发这个问题。
空格字符处理问题
输入内容开头或结尾的空格没有被正确处理。
代码片段
清单组件代码
"use client"; import { useState, useTransition } from "react"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { LoaderSpinner } from "@/components/loader-spinner"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { delay } from "@/lib/utils"; import { XIcon, Check, CircleMinus } from "lucide-react"; import { prepCheckList } from "@/lib/utils"; import { useRouter } from "next/navigation"; export function PreparationChecklist({ isChecklistOpen, setIsChecklistOpen, entityId, data, }) { const [isPending, startTransition] = useTransition(); const [isOpen, setIsOpen] = useState(false); const [items, setItems] = useState( data?.length > 0 ? data : [ { label: "label 1", checked: false, isNew: false, }, { label: "label 2", checked: false, isNew: false, }, { label: "label 3", checked: false, isNew: false, }, { label: "label 4", checked: false, isNew: false }, { label: "label 5", checked: false, isNew: false, }, { label: " label 6", checked: false, isNew: false, }, ], ); const [newField, setNewField] = useState(""); const [isSaving, setIsSaving] = useState(false); const [isAdding, setIsAdding] = useState(false); const route = useRouter(); const handleCheckedChange = (index, checked) => { setItems((prevItems) => { const updatedItems = [...prevItems]; updatedItems[index].checked = checked; return updatedItems; }); }; const handleSave = async () => { setIsSaving(true); const response = await prepCheckList(entityId, items); console.log("response: >>>>>>>>", response); await delay(1000); // Simulate API call startTransition(() => { route.refresh(); setIsSaving(false); }); }; const handleAddMoreClick = () => { if (isAdding) { if (newField) { // Add new item if input is not empty setItems((prevItems) => [ ...prevItems, { label: newField, checked: false, isNew: true }, ]); setNewField(""); // Reset the input field setIsAdding(false); // Hide the input field } else { // Hide input field if clicked again with no input setIsAdding(false); } } else { // Show input field setIsAdding(true); } }; const handleDeleteField = (index) => { // Only allow deleting items with `isNew: true` setItems((prevItems) => prevItems.filter((_, i) => !(i === index && prevItems[i].isNew)), ); }; return ( <Dialog open={isChecklistOpen} onOpenChange={() => setIsChecklistOpen(!isChecklistOpen)} > <DialogContent className="sm:max-w-[900px]"> <DialogHeader> <DialogTitle className="text-xl"> Checklist </DialogTitle> </DialogHeader> <div className="ml-2"> <ScrollArea className="h-[250px] rounded-md"> {items.map((item, index) => ( <div key={`${index}-${item.label}`} className="flex items-center gap-3 py-2" > <Checkbox id={`${index}-${item.label}`} checked={item.checked} onCheckedChange={(checked) => handleCheckedChange(index, checked) } className="h-4 w-4" /> <Label htmlFor={`${index}-${item.label}`} className="cursor-pointer" > {item.label} </Label> <div className="flex w-6 justify-center"> {item.isNew && ( <Button variant="link" size="smIcon" onClick={() => handleDeleteField(index)} > <CircleMinus className="h-4 w-4 text-red-500 hover:text-red-600" /> </Button> )} </div> </div> ))} </ScrollArea> </div> <div className="mt-4 flex items-center justify-end gap-4"> {isAdding && ( <Input value={newField} onChange={(e) => setNewField(e.target.value)} placeholder="Add new checklist item" className="flex-1" /> )} <Button size={isAdding ? "icon" : "sm"} variant={isAdding ? "outline" : "TypeA"} onClick={handleAddMoreClick} className={isAdding ? "rounded-full" : ""} > {isAdding ? ( newField ? ( <Check className="h-5 w-5 text-green-500 hover:text-green-600" /> ) : ( <XIcon className="h-5 w-5 text-red-500 hover:text-red-600" /> ) ) : ( "Add More" )} </Button> <Button size="sm" variant="TypeA" onClick={handleSave} disabled={isSaving || isPending} > {isSaving || isPending ? ( <LoaderSpinner className="text-white" /> ) : ( "Save changes" )} </Button> </div> <DialogFooter></DialogFooter> </DialogContent> </Dialog> ); }
Input组件封装代码
import * as React from "react"; import { cn } from "@/lib/utils"; const Input = React.forwardRef(({ className, type, ...props }, ref) => { return ( <input type={type} className={cn( "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", // placeholder:text-muted-foreground | placeholder-neutral-500 className, )} ref={ref} {...props} /> ); }); Input.displayName = "Input"; export { Input };
我尝试过的方法
- 防抖输入处理:我尝试过对输入处理函数做防抖,但字段在重新渲染时仍然会重置。
期望行为
- 输入字段(
newField)即使在items状态更新导致组件重新渲染时,也应该保留其值。 - 用户可以不受限制地输入空格(开头、结尾或中间)。
问题
- 如何防止输入字段(
newField)在重新渲染时重置? - 在确保验证干净的前提下,处理输入字段中空格的最佳方式是什么?
任何建议或替代方法都将不胜感激!谢谢。
备注:内容来源于stack exchange,提问作者Rafiul Rafi
相关产品推荐
相关产品推荐

