前言
第一次探索这个框架,对于里面很多逻辑是不懂的,所以只能一点一点去揣摩,其中做了什么。
而学习过程中,总是禁不住好奇这里的逻辑是干什么的,那里的逻辑是什么的,在不理解这段逻辑是做什么的情况下,死磕很容易事倍功半。所以本次先从一个比较简单的场景入手,看看它的源码中做了什么手脚,至于有些逻辑没有涉及到的,先不去管它就好了。
探究内容
效果
首先上图,看看这次案例的效果。
其实是上一篇案例的精简版,去掉了非空验证。但是分析的更细致些。
业务代码
import React from 'react';
import { createForm, formShape } from 'rc-form';
class Form extends React.Component {
static propTypes = {
form: formShape,
};
componentWillMount() {
this.nameDecorator = this.props.form.getFieldDecorator('name');
}
onSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((error, values) => {
if (!error) {
console.log('ok', values);
} else {
console.log('error', error, values);
}
});
};
onChange = (e) => {
console.log(e.target.value);
}
render() {
const { getFieldError } = this.props.form;
return (
<form onSubmit={this.onSubmit} style={{padding: '200px'}}>
{this.nameDecorator(
<input
onChange={this.onChange}
/>
)}
<div style={{ color: 'red' }}>
{(getFieldError('name') || []).join(', ')}
</div>
<button>Submit</button>
</form>
);
}
}
const WrappedForm = createForm()(Form);
export default WrappedForm;
源码分析
PS: 源码分析以代码+备注的形式展示
WrappedForm
概述
这个页面直接渲染了WrappedForm
,所以我们不妨直接从WrappedForm
看起。
其中WrappedForm
是由rc-form
提供的createForm创建的,第一个配置对象未传递,第二个参数是要修饰的组件。这里传递给了我们的业务组件
源码
createForm.js
import createBaseForm from './createBaseForm';
// 一系列给其他组件用的自定义混入
export const mixin = {
getForm() {
// 这里需要注意的是this是动态的,所以这里的this.xxx会根据环境改变而改变
return {
getFieldsValue: this.fieldsStore.getFieldsValue,
getFieldValue: this.fieldsStore.getFieldValue,
getFieldInstance: this.getFieldInstance,
setFieldsValue: this.setFieldsValue,
setFields: this.setFields,
setFieldsInitialValue: this.fieldsStore.setFieldsInitialValue,
getFieldDecorator: this.getFieldDecorator,
getFieldProps: this.getFieldProps,
getFieldsError: this.fieldsStore.getFieldsError,
getFieldError: this.fieldsStore.getFieldError,
isFieldValidating: this.fieldsStore.isFieldValidating,
isFieldsValidating: this.fieldsStore.isFieldsValidating,
isFieldsTouched: this.fieldsStore.isFieldsTouched,
isFieldTouched: this.fieldsStore.isFieldTouched,
isSubmitting: this.isSubmitting,
submit: this.submit,
validateFields: this.validateFields,
resetFields: this.resetFields,
};
},
};
function createForm(options) {
// 这里调用了createBaseForm并将混入传入到该函数
return createBaseForm(options, [mixin]);
}
export default createForm;
这里我们看到,其实createForm本身没做什么事情,只是在该文件内定义了混入的格式。
接下来我们的重点是createBaseForm中做了什么。这里只列出跟我们案例相关的内容
createBaseForm.js
//整体结构
function createBaseForm(options={}, mixins={} ) {
const {
mapPropsToFields,
onFieldsChange,
// ****其他的optinos里面的值,下文可能会用到
} = option;
//此处的WrappedComponent就是我们示例中,被包裹的Form组件
return funciton decorate(WrappedComponent) {
const Form = createReactClass({
// 该混入包含一个getForm方法用来得到一些通用方法
mixins,
getInitialState() {/*mark-init,初始化组件state*/}
componentWillReceiveProps(nextProps) {/*mark-recProps,初始化部分数据*/}
onCollect(){/*mark-collect收集表单数据*/}
onCollectCommon() {}
getCacheBind() {/*mark-bind组件事件绑定等收集*/}
getFieldDecorator() {/*mark-deco装饰组件,促进双向绑定的修饰器*/}
getFieldProps() {/*mark-props设置字段元数据,计算被修饰组件的属性*/}
// 一些其他函数
render() {
const { wrappedComponentRef, ...restProps } = this.props;
const formProps = {
[formPropName]: this.getForm(),
};
// ** 精简本次分析无关的代码
// 其中mapProps函数就是一个function(obj) {return obj};
// 这里用了一个小技巧,就是call(this,xxx),直接将该组件上的核心方法,全都放到了子组件的属性上,而且由于该组件是createReactClass创建的,所以子组件(本例中的Form)调用这些从父组件获取的方法时,方法内部的this,指向当前组件。
const props = mapProps.call(this, {
...formProps,
...restProps,
});
return <WrappedComponent {...props}/>;
},
},
})
//简化静态方法转移部分
return Form;
}
}
当createBaseForm函数在渲染函数中返回了我们的Form组件后,就可以看到Form组件中做的事情。
Form.js
class Form extends React.Component {
static propTypes = {
form: formShape,
};
componentWillMount() {
// 上个文件,createBaseForm的装饰器函数decorate,生成的组件中渲染了该组件,
// 并将getFieldDecorator方法通过属性传递给了它。
this.nameDecorator = this.props.form.getFieldDecorator('name');
}
onSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((error, values) => {
if (!error) {
console.log('ok', values);
} else {
console.log('error', error, values);
}
});
};
onChange = (e) => {
console.log(e.target.value);
}
render() {
const { getFieldError } = this.props.form;
return (
<form onSubmit={this.onSubmit} style={{padding: '200px'}}>
{this.nameDecorator(
<input
onChange={this.onChange}
/>
)}
{/*这里只是展示错误信息,不是我们关注点*/}
<div style={{ color: 'red' }}>
{(getFieldError('name') || []).join(', ')}
</div>
<button>Submit</button>
</form>
);
}
}
重点了。关键是看看getFieldDecorator中做了什么。
getFieldDecorator(name, fieldOption) {
// 获取需要传递给被修饰元素的属性。包括onChange,value等
// 同时在该props中设定用于收集元素值得监听事件(onChange),以便后续做双向数据。
const props = this.getFieldProps(name, fieldOption);
// 通过该函数传入(input/被修饰)元素。
return (fieldElem) => {
// 此处fieldStore存储字段数据信息以及元数据信息。
// 数据信息包括value,errors,dirty等
// 元数据信息包括initValue,defaultValue,校验规则等。
const fieldMeta = this.fieldsStore.getFieldMeta(name);
// 获取input上本身绑定的属性,例如该例子中的onChange打印内容函数
const originalProps = fieldElem.props;
fieldMeta.originalProps = originalProps;
fieldMeta.ref = fieldElem.ref;
return React.cloneElement(fieldElem, {
...props,
...this.fieldsStore.getFieldValuePropValue(fieldMeta),
});
};
},
其中getFieldProps
值得我们关注
// 简化后的内容如下
getFieldProps(name, usersFieldOption = {}) {
// name为我们为该文本框起的name
// 这里的数据fieldOption用来初始后面的FieldMeta。
const fieldOption = {
name,
trigger: 'onChange',
valuePropName: 'value', // checkBox取值时通过checked属性。
...usersFieldOption,
};
const {
trigger,
validateTrigger = trigger,
} = fieldOption;
// 第一次get元数据一般得不到,内部会返回个空对象
const fieldMeta = this.fieldsStore.getFieldMeta(name);
if ('initialValue' in fieldOption) {
fieldMeta.initialValue = fieldOption.initialValue;
}
// 这里的inputProps简化后结果为{value: xxx},第一次为空。
const inputProps = {
...this.fieldsStore.getFieldValuePropValue(fieldOption),
};
// make sure that the value will be collect
// 这里用来在getFieldDecorator第二个参数为空时,确保给input绑定一个基本的onChange事件来收集input的修改值,最终放入fieldsStore中
if (trigger && validateTriggers.indexOf(trigger) === -1) {
inputProps[trigger] = this.getCacheBind(name, trigger, this.onCollect);
}
// 这里就是我们fieldsStore中设置的元数据
const meta = {
...fieldMeta,
...fieldOption,
};
this.fieldsStore.setFieldMeta(name, meta);
// 这里返回的{value: 'xxx', onChange: fn};
return inputProps;
},
上述代码中onChange绑定了一个onCollect,其中getCacheBind函数主要是修正函数使用时候this指针。此处忽略直接分析onCollect
onCollect(name_, action, ...args) {
// 通过onCollectCommon在input的onChange中触发,收集到该元素相关东西
const { name, field, fieldMeta } = this.onCollectCommon(name_, action, args);
const { validate } = fieldMeta;
const newField = {
...field,
dirty: hasRules(validate),//根据规则验证,此处可忽略
};
// 更新fieldStore中的值,主要触发了一个forceUpdate方法,重新渲染该组件
this.setFields({
[name]: newField,
});
},
最后关键的一步就是看看onCollectCommon做了什么
onCollectCommon(name, action, args) {
const fieldMeta = this.fieldsStore.getFieldMeta(name);
if (fieldMeta[action]) {
fieldMeta[action](...args);
} else if (fieldMeta.originalProps && fieldMeta.originalProps[action]) {
// 此处调用input原来的onChange事件,即打印输入值,这个originalProps是在getFieldDecorator函数中存下的,这里只不过拿来用了。
fieldMeta.originalProps[action](...args);
}
// 此处的getValueFromEvent其实就是取e.target.value.
const value = fieldMeta.getValueFromEvent ?
fieldMeta.getValueFromEvent(...args) :
getValueFromEvent(...args);
const field = this.fieldsStore.getField(name);
return ({ name, field: { ...field, value, touched: true }, fieldMeta });
},
至此整个数据流程基本跑通,onChange
触发onCollect
去改变fieldStore
中的值并forceUpdate
更新界面,onCollectCommon
则展示了onCollect
取值的细节。forceUpdate
更新组件后,触发Form
的render
方法,又开始了之前getFieldDecorator
中读取fieldStore中值,返回被修改后的组件的流程。
题外话
跑通了最简单的场景,就可以向下一步更复杂的场景探索了。
结语
至此一个最简单的流程已经分析完毕。接下来,需要考虑的就是表单验证,数据反显(从后端拿到数据渲染编辑页面)等等,一步一个脚印,慢慢来吧。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。