![089ac84b84949f0ce249550591b2028b.png](https://img-blog.csdnimg.cn/img_convert/089ac84b84949f0ce249550591b2028b.png)
最近开发中使用到了Antd这款世界上最受欢迎的(前段时间还是第二)React-UI框架,而这个表单(Form)我看眉清目秀的,来,让哥哥来康康······
功能
- 收集输入表单容器(input、select)内容
- 根据校验规则进行内容校验 并 显示结果
API
- create
- getFieldDecorator
- getFieldValue
- getFieldsValue
- setFieldsValue
- ······(to do)
路径/components/form/Form.tsx
import createDOMForm from 'rc-form/lib/createDOMForm';
...
static create = function create<TOwnProps extends FormComponentProps>(
options: FormCreateOption<TOwnProps> = {},
): FormWrappedProps<TOwnProps> {
return createDOMForm({
fieldNameProp: 'id',
...options,
fieldMetaProp: FIELD_META_PROP,
fieldDataProp: FIELD_DATA_PROP,
});
};
我们可以看到,Antd 的Form 其实是调用了一个叫做 createDOMForm
的方法,而createDOMForm
实来自于rc-form(同为蚂蚁开发的一个react HOC form 组件),于是我知道,Antd-Form 实际是对 rc-form 进行的UI封装;
所以这里直接从 rc-formcreateDOMForm
方法来康起即可。
rc-form
React High Order Form Component
路径:/src/createDOMForm.js
无论是 Antd的 create 还是rc-form 本身的createForm 全都基于createBaseForm
的实现;
路径:/src/createBaseForm.js
645 行代码着实吓我一跳,但是仔细观之,其实通过简化还是可以发现:
function createBaseForm(option = {}, mixins = []) {
return function decorate(WrappedComponent) {
const Form = createReactClass({
render() {
return <WrappedComponent {...props} />
}
});
return Form;
}
}
这其实是一个高阶组件(HOC)的形式
那么问题来了:什么是高阶组件(HOC)呢?(我很少实际代码中写HOC)
说白了,就是一个函数,这个函数的样子是
function HOC (WrappedComponent) {
return class xxx extends React.Component {
constructor(props) {
...
}
render() {
return (
<WrappedComponent
ref={this.props.wrappedComponentRef}
/>
)
}
}
}
传入:组件
输入:组件
在组件上挂载一些props或者实现一些业务逻辑从而返回一个新的组件,UI由元组件提供;
那么接续上文 createBaseForm 其实就是将元组件(Form 容器)包裹实现各种新逻辑(如 上问题到的各种API方法)从而返回新的Form组件;
接下来具体问题具体看,使用Form组件的时候一般长用到几个方法:
- getFieldDecorator
- getFieldValue
- getFieldsValue
- setFieldsValue
- 等等(to do)
getFieldDecorator
因为之前有用Fusion
(react-ui组件框架),他的表单绑定写法是:
const init = this.field.init;
return (
<div>
<Input {...init('name',{initValue:'first value'})} />
<button onClick={this.onClick}>获取数据</button>
</div>
)
同样,rc-form 也提供了一种类似于这种绑定表单元素的方法,用这种方法给每个表单元素上绑定【名称】【校验规则】【初始值】······各种options
// getFieldProps
<form>
<input {...getFieldProps('name', { ...options })} />
</form>
// option
option.valuePropName
option.getValueProps
option.initialValue
option.rules
...
而 getFieldDecorator 方法呢,似乎更加优雅,他就类似于之前的createBaseForm
是一个HOC:
把 option.valuePropName,option.getValueProps,option.initialValue,option.rules等各种props挂在 被包含的表单组件上;
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
/>,
)}
源码同样简化一下:
getFieldDecorator(name, fieldOption) {
const props = this.getFieldProps(name, fieldOption);
// 用HOC的方式把props挂在被包含组件上
return (fieldElem) => {
return React.cloneElement(fieldElem, {
...props,
...this.fieldsStore.getFieldValuePropValue(fieldMeta),
});
};
}
// 这里出现了一个 fieldsStore,this.fieldsStore = createFieldsStore(fields || {});
// 在创建组件的过程中,将各个属性方法挂载在被包含组件上,而某些具体的方法实现则是在 createFieldsStore 中完成的;
那么问题来,createFieldsStore 是干嘛的?(存储、读取、设置表单数据)
打开瞧瞧:
路径:/src/createFieldsStore.js
原来,像getFieldsValue,getFieldValue,setFieldsInitialValue等用到的对于表单数据的 获取,存储等操作全是在这个文件中包裹的;
getFieldValue
功能: 功能是从存储的数据中,查找出指定表单名的数据
调用链:getFieldValue(name) -> getNestedField(name) [// 根据传入的name,调用getter函数(getValueFromFields)获取值] -> getValueFromFields (fullName, fields)
//
getValueFromFields(name, fields) {
const field = fields[name];
if (field && 'value' in field) {
return field.value;
}
const fieldMeta = this.getFieldMeta(name);
return fieldMeta && fieldMeta.initialValue;
}
// fields 从 createBaseForm 中来
const {
validateMessages,
onFieldsChange,
onValuesChange,
mapProps = identity,
mapPropsToFields, // ***
fieldNameProp,
fieldMetaProp,
fieldDataProp,
formPropName = 'form',
name: formName,
withRef,
} = option;
const fields = mapPropsToFields && mapPropsToFields(this.props);
// 通过使用 onFieldsChange 与 mapPropsToFields,可以把表单的数据存储到上层组件或者 Redux、dva 中
setFieldsValue
// 明天接着更新