第一次探索這個框架,對於裏面不少邏輯是不懂的,因此只能一點一點去揣摩,其中作了什麼。
而學習過程當中,老是禁不住好奇這裏的邏輯是幹什麼的,那裏的邏輯是什麼的,在不理解這段邏輯是作什麼的狀況下,死磕很容易事倍功半。因此本次先從一個比較簡單的場景入手,看看它的源碼中作了什麼手腳,至於有些邏輯沒有涉及到的,先不去管它就行了。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中值,返回被修改後的組件的流程。
跑通了最簡單的場景,就能夠向下一步更復雜的場景探索了。
至此一個最簡單的流程已經分析完畢。接下來,須要考慮的就是表單驗證,數據反顯(從後端拿到數據渲染編輯頁面)等等,一步一個腳印,慢慢來吧。