Controller包裹的自定义Input组件onChange失效及优化方案问询
解决方案:高效实现带状态切换的可编辑输入框
核心优化思路
- 每个输入框维护独立本地状态,避免依赖全局表单状态引发的过度重渲染
- 基于react-hook-form的
register绑定输入框,摒弃useWatch/useFormState的全局依赖 - 用本地状态控制编辑模式、按钮切换,仅在提交成功后同步表单并重置状态
修改后的可编辑输入框组件
import { useState } from 'react'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { PenToSquare, Check } from 'lucide-react'; import { UseFormRegisterReturn } from 'react-hook-form'; type EditableInputProps = { register: UseFormRegisterReturn; initialValue: string; onSubmit: (value: string) => Promise<void>; }; export function EditableInput({ register, initialValue, onSubmit }: EditableInputProps) { const [isEditing, setIsEditing] = useState(false); const [tempValue, setTempValue] = useState(initialValue); const [hasChanged, setHasChanged] = useState(false); // 触发编辑模式 const enterEditMode = () => { setIsEditing(true); setHasChanged(false); }; // 输入变化时更新临时值并标记修改状态 const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const value = e.target.value; setTempValue(value); setHasChanged(value !== initialValue); }; // 提交修改并重置状态 const confirmChanges = async () => { if (!hasChanged) { setIsEditing(false); return; } try { await onSubmit(tempValue); // 提交成功后同步到表单并重置本地状态 register.onChange({ target: { name: register.name, value: tempValue } } as React.ChangeEvent<HTMLInputElement>); setIsEditing(false); setHasChanged(false); } catch (err) { console.error('提交失败:', err); } }; return ( <div className="flex items-center gap-2"> <Input {...register} value={isEditing ? tempValue : initialValue} readOnly={!isEditing} onChange={handleInputChange} disabled={!isEditing} /> {isEditing ? ( hasChanged ? ( <Button size="icon" onClick={confirmChanges}> <Check className="h-4 w-4" /> </Button> ) : ( <Button size="icon" onClick={() => setIsEditing(false)} variant="secondary"> <PenToSquare className="h-4 w-4" /> </Button> ) ) : ( <Button size="icon" onClick={enterEditMode} variant="secondary"> <PenToSquare className="h-4 w-4" /> </Button> )} </div> ); }
父组件中使用示例
import { useForm } from 'react-hook-form'; import { EditableInput } from './editable-input'; type FormValues = { username: string; email: string; }; export function ProfileForm() { const { register, handleSubmit } = useForm<FormValues>({ defaultValues: { username: 'simo_d', email: 'simo@example.com', }, }); // 全局表单提交逻辑 const submitForm = async (data: FormValues) => { await new Promise(resolve => setTimeout(resolve, 1000)); console.log('表单提交完成:', data); }; // 单个输入框的提交回调 const handleFieldSubmit = (field: keyof FormValues) => async (value: string) => { await handleSubmit(submitForm)(); }; return ( <form className="space-y-4"> <EditableInput register={register('username')} initialValue={register('username').value as string} onSubmit={handleFieldSubmit('username')} /> <EditableInput register={register('email')} initialValue={register('email').value as string} onSubmit={handleFieldSubmit('email')} /> </form> ); }
问题解决说明
- 值变化检测:通过本地临时值与初始值的对比,准确判断输入内容是否修改,替代不可靠的全局
onChange监听 - 避免过度重渲染:所有状态(编辑模式、临时值、修改标记)都保存在组件内部,仅当自身状态变化时才触发重渲染,与全局表单状态解耦
- 符合交互逻辑:仅在提交成功后才恢复只读状态,未修改时点击按钮直接退出编辑模式
内容的提问来源于stack exchange,提问作者Simo D.
相关产品推荐
相关产品推荐

