在 <FormItem/> 中 不需要显式给 <Input>传递onChange 进行数据绑定的原因 ?
antd的<Form/>组件,自带数据域管理,所以不需要在<Input/> 去显式的添加onChange方法,去管理数据的绑定。
<Form.Item label="Username name="username" >
//并没有显示的传递 onchange方法。但组件内可以获取到 props.onChange 去传递修改的值
<Input />
</Form.Item>
原因是: FormItem 是通过使用 React.cloneElement的方法 clone 了一个新的 Element,去替换老的 Element, 在这个新的Element 的 props上去添加了传递过来的onChange事件。源码链接
//给childProps 挂载对于 control 里的 props
triggers.forEach(eventName => {
childProps[eventName] = (...args: any[]) => {
mergedControl[eventName]?.(...args);
children.props[eventName]?.(...args);
};
});
childNode = (
<MemoInput
value={mergedControl[props.valuePropName || 'value']}
update={updateRef.current}
>
//调用 cloneElement 创建新的 挂载上 onChange 的 element去代替原来写的。
{cloneElement(children, childProps)}
</MemoInput>
}
//替换Element的源码
export function replaceElement(
element: React.ReactNode,
replacement: React.ReactNode,
props: RenderProps,
): React.ReactNode {
if (!isValidElement(element)) return replacement;
return React.cloneElement(
element,
typeof props === 'function' ? props(element.props || {}) : props,
);
}
export function cloneElement(element: React.ReactNode, props?: RenderProps): React.ReactElement {
return replaceElement(element, element, props) as React.ReactElement;
}
为什么在新的Element添加 onChange去替换, 而不是在原Element的props上挂载onChange方法
例如
const onChange = () => {};
const element = React.createElement('div', { style: {width: '20px'} }, '*')
element.props.onChange = onChange;
尝试运行上面的方法会报 object is not extensible 的错误。
具体原因是 ReactElement 在描述一个JSX 时候 调用了 Object.freeze(element.props) 使props 冻结,不可扩展。
ReactElement.js 源码
const ReactElement = function (type, key, ref, self, source, owner, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE,
type,
key,
ref,
props,
_owner: owner,
};
if (Object.freeze) {
// 冻结 不可扩展
Object.freeze(element.props);
Object.freeze(element);
}
return element;
};
在自定义表单控件的时候,需要使用上FormItem的数据管理能力 , children 需注意必须是单独的,而不是一个数组
像下面的情况如果 FormItem 内是一个数组的话 自定义的组件,是拿不到 传递过来的 onChange方法,必须要单独使用。
<Form.Item name="name" >
<CustomInput/> // 自定义组件
<div>新建</div>
</Form.Item>
//单独使用
<Form.Item name="name" >
<CustomInput/> // 自定义组件
</Form.Item>
原因是: 源码中如果判断children 是一个数组的话,会直接赋值,而不会重新Clone 替换。
if (Array.isArray(children) && hasName) {
childNode = children;
}
抖音电商火热内推:
我们是字节跳动 - 抖音电商业务研发团队,主要负责字节系平台(抖音、火山、头条、西瓜等)电商核心级商业产品的研发工作,你的每一行代码,都在影响亿级消费者。
我们在北京、上海、杭州、武汉都有 office 。
招聘 前端工程师,客户端工程师,后端工程师,大量HC。