Arco Design Form 表单外部控制表单项错误状态:setFields 方法实战指南

原创2026-10-12 12:07:371,905 阅读
文章标签:UI组件前端设计系统

Arco Design Form 表单外部控制表单项错误状态:setFields 方法实战指南

本文聚焦 Arco Design React 组件库 Form 表单的 setFields 方法,讲解如何通过该方法在表单外部(而非依赖用户交互)控制表单项的错误状态、警告信息与字段值。你将掌握 setFields 的完整参数结构、与 setFieldValue / setFieldsValue / resetFields 等方法的配合用法,以及错误状态在底层 Store 与 Control 中的流转机制,从而在联动校验、服务端返回错误、异步数据回填等场景下写出可靠的受控表单。

核心示例:通过 setFields 控制表单项错误状态

Arco Design Form 组件(仓库路径:components/Form)提供 Form.useForm() 获取表单实例,其中 setFields 方法允许你在任意时刻(事件回调、useEffect、接口回调等外部逻辑中)直接设置指定表单项的值、错误信息和警告信息。

官方演示 components/Form/demo/setFields.md 给出了完整可运行的示例:

import { Form, Input, Button, InputNumber } from '@arco-design/web-react';
const FormItem = Form.Item;

function App() {
  const [form] = Form.useForm();
  return (
    <Form
      form={form}
      style={{ width: 600 }}
      initialValues={{ name: 'admin' }}
      autoComplete="off"
      onValuesChange={(v, vs) => {
        console.log(v, vs);
      }}
      onSubmit={(v) => {
        console.log(v);
      }}
    >
      <FormItem label="Username" field="name" rules={[{ required: true }]}>
        <Input placeholder="please enter your username" />
      </FormItem>
      <FormItem
        label="Age"
        field="age"
        rules={[{ required: true, type: 'number', min: 0, max: 99 }]}
      >
        <InputNumber placeholder="please enter your age" />
      </FormItem>
      <FormItem wrapperCol={{ offset: 5 }}>
        <Button type="primary" htmlType="submit" style={{ marginRight: 24 }}>
          Submit
        </Button>
        <Button
          style={{ marginRight: 24 }}
          onClick={() => {
            form.resetFields();
          }}
        >
          Reset
        </Button>
        <Button
          type="text"
          onClick={() => {
            form.setFields({
              age: {
                value: 200,
                error: {
                  message: 'Maximum is 200',
                },
                warning: <div>warning info ...</div>,
              },
            });
          }}
        >
          Set Error Age
        </Button>
      </FormItem>
    </Form>
  );
}

export default App;

点击 "Set Error Age" 按钮后,age 字段被设置为 200(超出 rules 中 max: 99 的限制),同时外部强制写入错误信息 "Maximum is 200" 与警告节点。此时 Form.Item 会呈现 error 校验状态,错误文案显示在表单项下方。

setFields 方法签名与参数解析

根据 components/Form/README.zh-CN.md 的方法表,setFields 的类型为:

Function({ [field]: string: { value: any, error: FieldError } })

在 components/Form/store.tsx 中,setFields 的真实实现签名如下:

public setFields = (obj: {
  [field in FieldKey]?: {
    value?: FieldValue;
    error?: FieldError<FieldValue>;
    touched?: boolean;
    warning?: React.ReactNode;
  };
}) => { ... }

FieldError 的定义位于 components/Form/interface.tsx:

export type FieldError<FieldValue = any> = {
  value?: FieldValue;
  message?: ReactNode;
  type?: string;
  requiredError?: boolean;
};

各参数含义如下:

参数 类型 说明
value FieldValue 要写入该字段的新值,会更新表单内部 store 并触发 onValuesChange
error FieldError 错误信息对象,核心字段为 message(ReactNode,可传字符串或任意 React 节点);也支持 type、requiredError 等扩展字段
warning React.ReactNode 警告信息,可传字符串或 JSX 节点;warning 状态不会阻塞表单提交
touched boolean 是否标记字段被"操作过",影响 getTouchedFields 的返回值

结合 store.tsx 中 setFields 的实现 可以看到,setFields 会遍历传入对象的每个字段,将 error / warning / touched / value 分别收集进 StoreChangeInfo['data'],写入 store 后通过 notify('setFieldValue', ...) 通知对应 Control,最后触发 triggerValuesChange(changeValues) 回调 onValuesChange。

错误状态在底层的流转机制

为了正确使用 setFields,理解错误状态如何"到达" Form.Item 很有价值。

Control:每个字段的控制器

每个 Form.Item 对应一个 Control 实例(components/Form/control.tsx)。当 setFields 触发 setFieldValue 通知时,Control.onStoreChange 会读取 info.data:

case 'setFieldValue':
  if (isFieldMatch(field, fields)) {
    this.toggleTouched(true);
    if (info.data && 'touched' in info.data) {
      this.toggleTouched(info.data.touched);
    }
    if (info.data && 'warnings' in info.data) {
      this.setWarnings(
        isNullOrUndefined(info.data.warnings) ? [] : [].concat(info.data.warnings)
      );
    }
    if (info.data && 'errors' in info.data) {
      this.setErrors(info.data.errors);
    } else if (!isEqualWith(get(info.prev, field), get(info.current, field))) {
      this.setErrors(null);
    }
    this.updateFormItem();
    return;
  }
  ...

值得注意的两个细节:

  1. 传入 error 时:直接调用 setErrors(info.data.errors),完全以外部传入的错误信息为准(不会因为值变化而自动清除)。
  2. 未传入 error 但值发生变化时:会通过 isEqualWith 对比新旧值,若值确实改变则 setErrors(null) 清空错误——这正是"修改值自动清除旧错误"行为的来源。
  3. warning 为 null 或 undefined 时会被归一为 [] 清空警告;传字符串或节点时会被 [].concat() 包装为数组。

Form.Item:渲染错误与警告

components/Form/form-item.tsx 汇总各字段的 errors / warnings:

const errorInfo = errors ? Object.values(errors) : [];
const warningInfo = warnings
  ? Object.values(warnings).reduce((total, next) => total.concat(next), [])
  : [];

const itemStatus = useMemo(() => {
  if (errorInfo.length) {
    return VALIDATE_STATUS.error;
  }
  if (warningInfo.length) {
    return VALIDATE_STATUS.warning;
  }
  ...
}, [errors, warnings, validateStatus]);

当 errorInfo 非空时,Form.Item 的校验状态变为 error,并通过 FormItemTip 组件渲染错误与警告文案(message-help-error 与 message-help-warning 两个类名分别对应错误/警告提示块)。

setFields 与其他表单方法的配合

与 setFieldValue / setFieldsValue 的关系

从 store.tsx 的实现可以看到:

// 外部调用设置表单字段值
public setFieldValue = (field: FieldKey, value: FieldValue) => {
  if (!field) return;
  this.setFields({
    [field]: { value },
  } as any);
};

// 外部调用,设置多个表单控件的值
public setFieldsValue = (values: DeepPartial<FormData>) => {
  if (isObject(values)) {
    const fields = Object.keys(values);
    const obj = {};
    fields.forEach((field) => {
      obj[field] = { value: values[field] };
    });
    this.setFields(obj);
  }
};

可见 setFieldValue 与 setFieldsValue 都是 setFields 的简化封装:它们仅设置 value,不携带错误信息。setFields 则是三者中能力最完整的"底层"方法——既设置值,也能注入错误与警告。

与 resetFields 的配合

resetFields 在 store.tsx 中将字段重置为 initialValues(或 initialValue),并触发 notify('reset', ...) 清空 touchedFields。演示示例中 "Reset" 按钮即调用 form.resetFields() 将 name 恢复为 'admin'、age 恢复为初始值,同时清空之前 setFields 注入的错误状态(因为 Control 在 reset 时会 updateFormItem 并清除 errors)。

与 clearFields 的对比

README.zh-CN.md 中说明:

clearFields:清除表单控件的值以及校验状态。等同于 form.setFields({ [field]: { value: undefined; error: null, warning: null } })(版本 2.29.0 起)

这正好印证了 setFields 的 error: null、warning: null 可用来主动清除校验状态。

典型应用场景

场景一:服务端返回错误回填

后端接口校验失败时,把返回的错误信息通过 setFields 直接注入对应字段:

const [form] = Form.useForm();

async function handleSubmit() {
  try {
    await api.submit(form.getFields());
  } catch (e) {
    // 例如 e.errors = { email: '该邮箱已被注册' }
    form.setFields({
      email: { error: { message: e.errors.email } },
    });
  }
}

场景二:异步数据回填

表单数据为异步加载时,用 setFields 一次性设置值与校验状态。注意 README.zh-CN.md 的提醒:initialValue 只会在表单挂载时生效,异步数据请用 setFieldValue / setFieldsValue / setFields 更新。

useEffect(() => {
  api.fetchUser().then((user) => {
    form.setFields({
      name: { value: user.name },
      age: { value: user.age },
    });
  });
}, []);

场景三:组件挂载时机注意

README.zh-CN.md 常见问题中强调:若在 Modal 未挂载时调用 form.setFieldsValue({}) 等实例方法会不生效,因为此时 Form 尚未创建。同理适用于 setFields——调用时机需保证 Form 已挂载(或设置 Modal 的 mountOnEnter={false})。

边界行为与实现细节

  • error 优先级高于 warning:Form.Item 的最终状态由 form-item.tsx 中的优先级决定——有 error 时显示 error,否则有 warning 显示 warning,两者都无且设置了 help 时展示 help 提示。
  • warning 不阻塞提交:RulesProps 中的 validateLevel: 'warning' 不会阻塞表单提交;同理 setFields 写入的 warning 只作为非阻塞提示展示。
  • 校验状态联动:Control 在 gotoValidatingStatus 中会先清空 errors/warnings 再进入 validating 状态,因此 setFields 注入的错误会在下次校验时被覆盖(详见 control.tsx)。
  • 测试覆盖:仓库 components/Form/test/setfield.test.tsx 验证了 setFieldsValue 对空字符串、空数组等值的收集行为;变更日志 components/Form/changelog/index.zh-CN.md 记录了 setFields 传入 warning: null 时未清空 warning 状态的修复、以及 formList 中 setFields 错误状态索引错位的修复,说明该方法在复杂表单(嵌套字段、Form.List)中同样受支持。

小结

setFields 是 Arco Design Form 在外部驱动表单项状态的核心方法:它一次调用即可完成"设值 + 注入错误 + 注入警告 + 标记 touched"四件事,是 setFieldValue / setFieldsValue 的能力超集。结合 resetFields、clearFields 与 Form.Item 的渲染机制,你可以完全在表单外部编排错误展示,实现服务端校验反馈、异步回填、联动校验等交互,而不必依赖用户操作触发的内置校验流程。

登录后查看全文
arco-design