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

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>
  );
}

问题解决说明

  1. 值变化检测:通过本地临时值与初始值的对比,准确判断输入内容是否修改,替代不可靠的全局onChange监听
  2. 避免过度重渲染:所有状态(编辑模式、临时值、修改标记)都保存在组件内部,仅当自身状态变化时才触发重渲染,与全局表单状态解耦
  3. 符合交互逻辑:仅在提交成功后才恢复只读状态,未修改时点击按钮直接退出编辑模式

内容的提问来源于stack exchange,提问作者Simo D.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 03:33:25