首页>>前端>>JavaScript->Antd Form.Item name 属性不生效问题(含原理解析)

Antd Form.Item name 属性不生效问题(含原理解析)

时间:2023-12-02 本站 点击:0

问题

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

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 的代码如下:

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

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,可能会存在一些性能问题

解决方法二

其实官方也有提到

被设置了 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 参数,所以并没有生效,所以可以修改成如下:

- 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()

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

React.cloneElement() 几乎等同于:

<element.type {...element.props} {...props}>{children}</element.type>

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

核心代码的实现如下:

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感兴趣么

原文:https://juejin.cn/post/7103472687170715684


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/JavaScript/9550.html