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

React-Hook-Form中getValues与setValue无法获取完整当前值问题

React-Hook-Form动态数组字段丢失title值问题解决

问题概述

使用React-Hook-Form处理依赖多源动态数据的复杂表单时,遇到以下问题:

  • 表单包含动态数组goals,数组项通过开关控制是否显示备注字段。
  • 仅当前会话中切换过开关的goals项,其title字段能在getValues()结果中保留;历史已标记为true的项(已包含在defaultValues中)的title会丢失,导致提交前传递给签名组件的数据不完整。
  • 不能要求用户每次提交前重新切换历史项,需要保持良好的UX并复用签名组件。

原因分析

  1. 表单状态跟踪逻辑缺失:React-Hook-Form仅跟踪与表单控件绑定的字段,title未绑定到任何控件,仅在开关切换时通过setValue手动添加到表单状态,历史项未触发此逻辑,因此未被跟踪。
  2. defaultValues未完全生效:虽然defaultValues包含完整的goals数组,但未显式将title同步到表单状态,导致未绑定控件的字段被忽略。

解决方案

方案:初始化时同步所有历史项的title到表单状态

在父组件初始化完成后,遍历goals数组,对每个已标记为true的项,调用setValue将title写入表单状态,确保所有历史项的title被表单跟踪。

修改后的代码示例

父组件(EditVisit)

const EditVisit = () => {
  const hasVisit = useSelector(state => state.visit);
  const goals = hasVisit.goals;
  
  const { register, handleSubmit, control, setValue, watch, getValues, formState: { errors, dirtyFields } } = useForm({
    defaultValues: {
      visitStart: hasVisit.visitStart,
      visitEnd: hasVisit.visitEnd,
      location: hasVisit.location,
      goals: hasVisit.goals,
      status: hasVisit.status,
      cancelNote: hasVisit.cancelNote
    },
    shouldUnregister: true
  });

  // 初始化同步已标记项的title到表单状态
  useEffect(() => {
    if (goals?.length) {
      goals.forEach((goal, index) => {
        if (goal.marked) {
          setValue(`goals.${index}.title`, goal.title);
        }
      });
    }
  }, [goals, setValue]);

  const onSubmit = (data) => {
    console.log("提交数据:", data);
    // 传递data给签名组件并完成提交
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* 其他表单字段 */}
      {goals.length >= 1 ? (
        goals.map((goal, index) => (
          <GoalInput 
            key={index}
            goal={goal} 
            index={index} 
            control={control}
            errors={errors}
          />
        ))
      ) : (
        <Typography>No service plan with goals for this date. Goals must be set in the plan to reflect here.</Typography>
      )}
      {/* 签名组件和提交按钮 */}
    </form>
  );
};

GoalInput组件

function GoalInput({ goal, index, control, errors }) {
  // 直接用goal.marked初始化toggle状态
  const [toggle, setToggle] = useState(goal.marked === true);

  // 监听goal.marked的外部变化,同步toggle状态
  useEffect(() => {
    setToggle(goal.marked === true);
  }, [goal.marked]);

  return (
    <React.Fragment key={index}>
      <Controller
        control={control}
        type="checkbox"
        name={`goals.${index}.marked`}
        defaultValue={goal?.marked}
        render={({ field }) => (
          <Switch
            checked={toggle}
            onChange={(e) => {
              field.onChange(e);
              setToggle(!toggle);
            }}
            value={field.value}
          />
        )}
      />
      <label>{goal?.title}</label>
      <br />
      {toggle ? (
        <>
          <Controller
            control={control}
            name={`goals.${index}.note`}
            id={`goals.${index}.note`}
            defaultValue={goal?.note}
            render={({ field }) => (
              <TextField
                index={index}
                error={!!errors?.goals?.[index]?.note}
                value={field.value}
                onChange={(e) => field.onChange(e)}
                label="Progress Note"
              />
            )}
          />
          <br />
        </>
      ) : null}
    </React.Fragment>
  );
}

效果验证

修改后,getValues()返回的goals数组将包含所有已标记项的title字段,无论是否在当前会话中切换过开关,确保传递给签名组件的数据完整。

内容的提问来源于stack exchange,提问作者Jowz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 07:53:15