前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Antd 中 Form.Item name 属性不生效问题

Antd 中 Form.Item name 属性不生效问题

作者头像
GopalFeng
发布2022-08-01 20:01:41
2.3K0
发布2022-08-01 20:01:41
举报
文章被收录于专栏:前端杂货铺-Gopal

问题

Form.Item 中设置了 name 属性,但是 Form 中的 onValuesChange 并没有生效。简单代码如下,可以看 codesanbox[1] 示例:

代码语言:javascript
复制
const schemaList = [
  {
    label: "Name",
    field: "name",
    component: Input,
    rules: [{ required: true, message: "Name is required" }],
    props: {
      showCount: true,
      maxLength: 30
    }
  }
];

const Demo = () => {
  const [form] = Form.useForm();

  const onValueChange = () => {
    console.log("test");
  };

  return (
    <Form name="basic" form={form} onValuesChange={onValueChange}>
      {schemaList.map((item) => {
        // 方法二:修改
        // const component = getBasicFormItem(form.getFieldsValue(true), item);
        return (
          <Form.Item label={item.label} name={item.field} rules={item.rules}>
            {/* 方法二修改 */}
            {/* {component} */}
            <BasicFormItem form={form.getFieldsValue(true)} schema={item} />
          </Form.Item>
        );
      })}
    </Form>
  );
};

BasicFormItem 的代码如下:

代码语言:javascript
复制
const BasicFormItem = ({ form, schema }) => {
  if (schema.component) {
    const Component = schema.component;
    return <Component {...schema.props}></Component>;
  } else {
    return form[schema.field] !== undefined ? form[schema.field] : "-";
  }
};

解决方法

解决方法一

尝试将上面的 function Component 写成一个返回组件的 function

代码语言:javascript
复制
const getBasicFormItem = (form, schema) => {
  if (schema.component) {
    const Component = schema.component;
    return <Component {...schema.props}></Component>;
  } else {
    return form[schema.field] !== undefined ? form[schema.field] : "-";
  }
};

// 调用的时候返回一个组件
const component = getBasicFormItem(form.getFieldsValue(true), item);
return (
  <Form.Item label={item.label} name={item.field} rules={item.rules}>
    {/* 方法二修改 */}
    {component}
    <BasicFormItem form={form.getFieldsValue(true)} schema={item} />
  </Form.Item>
);

这其实是一种比较 hack 的方法,而且每次都一定会去执行这个 function,返回一个全新的 component,可能会存在一些性能问题

解决方法二

其实官方[2]也有提到

被设置了 name 属性的 Form.Item 包装的控件,表单控件会自动添加 value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性),数据同步将被 Form 接管。这会导致以下结果:

1.你不再需要也不应该用 onChange 来做数据收集同步(你可以使用 Form 的 onValuesChange),但还是可以继续监听 onChange 事件。

2.你不能用控件的 value 或 defaultValue 等属性来设置表单域的值,默认值可以用 Form 里的 initialValues 来设置。注意 initialValues 不能被 setState 动态更新,你需要用 setFieldsValue 来更新。

3.你不应该用 setState,可以使用 form.setFieldsValue 来动态改变表单值。

但在上面 BasicFormItem 中,我只接收了 form 和 schema 参数,所以并没有生效,所以可以修改成如下:

代码语言:javascript
复制
- const BasicFormItem = ({ form, schema }) => {
+ const BasicFormItem = ({ form, schema, ...reset }) => {
  if (schema.component) {
    const Component = schema.component;
-    return <Component {...schema.props}></Component>;
+    return <Component {...reset} {...schema.props}></Component>;
  } else {
    return form[schema.field] !== undefined ? form[schema.field] : "-";
  }
};

这样就可以了

原理

问题来了,antd 是怎么做到将 value 和 onChange 注入的呢?

问题的答案在于:cloneElement()[3]

以 element 元素为样板克隆并返回新的 React 元素。config 中应包含新的 props,key 或 ref。返回元素的 props 是将新的 props 与原始元素的 props 浅层合并后的结果。

React.cloneElement() 几乎等同于:

代码语言:javascript
复制
<element.type {...element.props} {...props}>{children}</element.type>

以下为一个大神的简单版实现,可以看下,详情[4]

核心代码的实现如下:

代码语言:javascript
复制
let wrapperNode: React.ReactNode = React.cloneElement(
  children as React.ReactElement,
  {
    onChange(event) {
      if (event && event.target) {
        const newValue = (event.target as HTMLInputElement)['value'];
        onFieldChange!(name, newValue);
      }
    },
  },
);

这里就将 onChange 注入到子组件的 props 中了,然后变化的时候,再通知 Form 组件进行相应的更新

参考

  • 难道没人对Form.Item如何处理Input感兴趣么[5]

参考资料

[1]codesanbox: https://codesandbox.io/s/ji-ben-shi-yong-antd-4-19-5-forked-rxwn8f?file=/index.js:1256-1877

[2]官方: https://ant.design/components/form-cn/#Form.Item

[3]cloneElement(): https://zh-hans.reactjs.org/docs/react-api.html#cloneelement

[4]详情: https://codesandbox.io/s/fragrant-resonance-ghkbj1?file=/src/Form/Field.tsx

[5]难道没人对Form.Item如何处理Input感兴趣么: https://zhuanlan.zhihu.com/p/412418736

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端杂货铺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 解决方法
    • 解决方法一
      • 解决方法二
      • 原理
      • 参考
        • 参考资料
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档