第一次探索这个框架,对于里面不少逻辑是不懂的,因此只能一点一点去揣摩,其中作了什么。
而学习过程当中,老是禁不住好奇这里的逻辑是干什么的,那里的逻辑是什么的,在不理解这段逻辑是作什么的状况下,死磕很容易事倍功半。因此本次先从一个比较简单的场景入手,看看它的源码中作了什么手脚,至于有些逻辑没有涉及到的,先不去管它就行了。react
首先上图,看看此次案例的效果。后端
实际上是上一篇案例的精简版,去掉了非空验证。可是分析的更细致些。app
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
是由rc-form
提供的createForm建立的,第一个配置对象未传递,第二个参数是要修饰的组件。这里传递给了咱们的业务组件函数
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中作了什么。这里只列出跟咱们案例相关的内容源码分析
//总体结构 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组件中作的事情。学习
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中作了什么。this
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
值得咱们关注spa
// 简化后的内容以下 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中值,返回被修改后的组件的流程。
跑通了最简单的场景,就能够向下一步更复杂的场景探索了。
至此一个最简单的流程已经分析完毕。接下来,须要考虑的就是表单验证,数据反显(从后端拿到数据渲染编辑页面)等等,一步一个脚印,慢慢来吧。