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

react-hook-form移除字段后yup仍校验已注销字段的问题求助

解决react-hook-form结合yup动态移除字段后校验异常的问题

核心问题在于yup的静态schema和react-hook-form动态字段的不匹配:你用unregister移除了表单字段,但yup仍会按照初始写死的schema校验所有标记为必填的字段,哪怕这些字段已经不在表单values里。下面是直接可行的解决方案:


方法1:动态生成yup Schema(推荐)

维护一个当前存在的字段列表,每次添加/移除字段时,根据这个列表动态生成对应的yup schema,确保schema只包含当前需要校验的字段。

完整代码示例

import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import { useState, useMemo } from 'react';

function DynamicForm() {
  // 维护当前存在的字段列表,可根据需求扩展字段配置(比如是否必填)
  const [activeFields, setActiveFields] = useState([{ name: 'email', required: true }]);

  // 动态生成yup schema,依赖activeFields更新
  const formSchema = useMemo(() => {
    const schemaShape = {};
    activeFields.forEach(field => {
      schemaShape[field.name] = field.required
        ? yup.string().required(`${field.name} 不能为空`)
        : yup.string();
    });
    return yup.object().shape(schemaShape);
  }, [activeFields]);

  const { register, handleSubmit, unregister, formState: { errors } } = useForm({
    resolver: yupResolver(formSchema),
    defaultValues: activeFields.reduce((acc, field) => ({ ...acc, [field.name]: '' }), {})
  });

  // 添加字段逻辑
  const addField = () => {
    const newFieldName = `field_${Date.now()}`;
    setActiveFields([...activeFields, { name: newFieldName, required: true }]);
  };

  // 移除字段逻辑
  const removeField = (fieldName) => {
    setActiveFields(activeFields.filter(f => f.name !== fieldName));
    unregister(fieldName);
  };

  const onSubmit = (data) => {
    console.log('提交成功:', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {activeFields.map(field => (
        <div key={field.name} style={{ margin: '10px 0' }}>
          <input
            {...register(field.name)}
            placeholder={field.name}
          />
          {errors[field.name] && <span style={{ color: 'red' }}>{errors[field.name].message}</span>}
          <button type="button" onClick={() => removeField(field.name)} style={{ marginLeft: '10px' }}>移除</button>
        </div>
      ))}
      <button type="button" onClick={addField} style={{ marginRight: '10px' }}>添加字段</button>
      <button type="submit">提交表单</button>
    </form>
  );
}

export default DynamicForm;

关键说明

  • 用useMemo监听activeFields的变化,每次字段列表更新时重新生成schema,确保schema只包含当前存在的字段。
  • 移除字段时同时更新activeFields和调用unregister,让表单注册状态和schema完全同步。

方法2:使用yup的when条件校验

如果只是个别字段需要动态控制必填规则,可以用yup的when方法,让校验规则依赖于字段是否存在于表单values中:

const formSchema = yup.object().shape({
  dynamicField: yup.string().when('', {
    // 判断当前values中是否包含该字段
    is: (values) => Object.prototype.hasOwnProperty.call(values, 'dynamicField'),
    then: (schema) => schema.required('该字段为必填项'),
    otherwise: (schema) => schema.notRequired()
  })
});

这种方法适合字段数量少、规则简单的场景,字段较多时推荐用动态生成schema的方式。


方法3:处理数组型动态字段

如果你的动态字段是数组结构(比如动态添加的表单项),可以直接用yup的array().of()来定义schema,react-hook-form会自动识别数组项的添加/移除,yup只会校验当前数组中存在的项:

const formSchema = yup.object().shape({
  items: yup.array().of(
    yup.object().shape({
      name: yup.string().required('名称不能为空'),
      quantity: yup.number().required('数量不能为空').positive()
    })
  )
});

移除数组项时,调用unregister(items[${index}])即可,yup会自动跳过已移除的项的校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 07:05:22