Skip to main content

Antd Form.Item 中包裹组件 onChange 穿透

问题描述

在使用antd Form.Item的时候 嵌入Input这种表单组件,会默认在内部注入onChange、value等props
<Form>
<Form.Item name="name">
<Input />
</Form.Item>
</Form>

上面可以看到 Input 组件并没有用 onChange 和 value 来进行数据处理,所有的数据管理都由整个 Form 组件进行内部整合,整体看起来很比较整洁,没有额外的代码干扰

实现原理

探究疑问

  1. Input 组件作为一个 children 传递,如果做到给他传递 props 呢?
  2. Input 使用的时候加上了 onChange 等属性的时候,Form.Item 如何给Input 这个 children 传递 onChange 呢?不会被覆盖掉吗?

原理

问题 1

在 React 中存在一个 API cloneElement 方法,使用该方法可以 clone 一个新的 React 元素,同时可以传递 props。

代码示例

import { cloneElement } from "react";

const clonedElement = cloneElement(
<Row title="Cabbage">Hello</Row>,
{ isHighlighted: true },
"Goodbye"
);

console.log(clonedElement);

在 Form.Item 组件的源码中也可以看到是以该 API 进行处理 props 的 代码示例

<MemoInput
control={mergedControl}
update={mergedChildren}
childProps={watchingChildProps}
>
{cloneElement(mergedChildren, childProps)}
</MemoInput>

问题 2

在 Form.Item 源码中,构建了一个统一的 onChange 方法,将 Form.Item 内部传递的 onChange 方法,和 Input 直接传递的 onChange 方法,归于该方法里面统一调用。避免方法被覆盖掉倒是 Input 自身传入的 onChange 方法失效。

代码示例

// trigger 是描述 表单组件的 触发方法的 例如 onChange这种
const triggers = new Set<string>([
...toArray(trigger),
...toArray(mergedValidateTrigger),
]);

triggers.forEach((eventName) => {
childProps[eventName] = (...args: any[]) => {
mergedControl[eventName]?.(...args);
mergedChildren.props[eventName]?.(...args);
};
});

如上所述,我们可以在构建一些自定义的表单组件的时候,可以根据这一特性,去兼容 antd 的 Form 表单组件,减少复杂的表单数据管理操作。