如何封裝React Form組件?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
專注于為中小企業(yè)提供網(wǎng)站設計制作、成都網(wǎng)站建設服務,電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)隆陽免費做網(wǎng)站提供優(yōu)質的服務。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了1000多家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設實現(xiàn)規(guī)模擴充和轉變。
Form組件功能
一般來說,F(xiàn)orm組件的功能包括以下幾點:
表單布局
表單字段
封裝表單驗證&錯誤提示
表單提交
下面將對每個部分的實現(xiàn)方式做詳細介紹。
表單布局
常用的表單布局一般有3種方式:
行內布局

水平布局

垂直布局

實現(xiàn)方式比較簡單,嵌套css就行。比如form的結構是這樣:
<form class="form"> <label class="label"/> <field class="field"/> </form>
對應3種布局,只需要在form標簽增加對應的class:
<!--行內布局--> <form class="form inline"> <label class="label"/> <field class="field"/> </form> <!--水平布局--> <form class="form horizontal"> <label class="label"/> <field class="field"/> </form> <!--垂直布局--> <form class="form vertical"> <label class="label"/> <field class="field"/> </form>
相應的,要定義3種布局的css:
.inline .label {
display: inline-block;
...
}
.inline .field {
display: inline-block;
...
}
.horizontal .label {
display: inline-block;
...
}
.horizontal .field {
display: inline-block;
...
}
.vertical .label {
display: block;
...
}
.vertical .field {
display: block;
...
}表單字段封裝
字段封裝部分一般是對組件庫的組件針對Form再做一層封裝,如Input組件、Select組件、Checkbox組件等。當現(xiàn)有的字段不能滿足需求時,可以自定義字段。
表單的字段一般包括兩部分,一部分是標題,另一部分是內容。ZentForm通過getControlGroup這一高階函數(shù)對結構和樣式做了一些封裝,它的入?yún)⑹且@示的組件:
export default Control => {
render() {
return (
<div className={groupClassName}>
<label className="zent-form__control-label">
{required ? <em className="zent-form__required">*</em> : null}
{label}
</label>
<div className="zent-form__controls">
<Control {...props} {...controlRef} />
{showError && (
<p className="zent-form__error-desc">{props.error}</p>
)}
{notice && <p className="zent-form__notice-desc">{notice}</p>}
{helpDesc && <p className="zent-form__help-desc">{helpDesc}</p>}
</div>
</div>
);
}
}這里用到的label和error等信息,是通過Field組件傳入的:
<Field
label="預約門店:"
name="dept"
component={CustomizedComp}
validations={{
required: true,
}}
validationErrors={{
required: '預約門店不能為空',
}}
required
/>這里的CustomizedComp是通過getControlGroup封裝后返回的組件。
字段與表單之間的交互是一個需要考慮的問題,表單需要知道它包含的字段值,需要在適當?shù)臅r機對字段進行校驗。ZentForm的實現(xiàn)方式是在Form的高階組件內維護一個字段數(shù)組,數(shù)組內容是Field的實例。后續(xù)通過操作這些實例的方法來達到取值和校驗的目的。
ZentForm的使用方式如下:
class FieldForm extends React.Component {
render() {
return (
<Form>
<Field
name="name"
component={CustomizedComp}
</Form>
)
}
}
export default createForm()(FieldForm);其中Form和Field是組件庫提供的組件,CustomizedComp是自定義的組件,createForm是組件庫提供的高階函數(shù)。在createForm返回的組件中,維護了一個fields的數(shù)組,同時提供了attachToForm和detachFromForm兩個方法,來操作這個數(shù)組。這兩個方法保存在context對象當中,F(xiàn)ield就能在加載和卸載的時候調用了。簡化后的代碼如下:
/**
* createForm高階函數(shù)
*/
const createForm = (config = {}) => {
...
return WrappedForm => {
return class Form extends Component {
constructor(props) {
super(props);
this.fields = [];
}
getChildContext() {
return {
zentForm: {
attachToForm: this.attachToForm,
detachFromForm: this.detachFromForm,
}
}
}
attachToForm = field => {
if (this.fields.indexOf(field) < 0) {
this.fields.push(field);
}
};
detachFromForm = field => {
const fieldPos = this.fields.indexOf(field);
if (fieldPos >= 0) {
this.fields.splice(fieldPos, 1);
}
};
render() {
return createElement(WrappedForm, {...});
}
}
}
}
/**
* Field組件
*/
class Field extends Component {
componentWillMount() {
this.context.zentForm.attachToForm(this);
}
componentWillUnmount() {
this.context.zentForm.detachFromForm(this);
}
render() {
const { component } = this.props;
return createElement(component, {...});
}
}當需要獲取表單字段值的時候,只需要遍歷fields數(shù)組,再調用Field實例的相應方法就可以:
/**
* createForm高階函數(shù)
*/
const createForm = (config = {}) => {
...
return WrappedForm => {
return class Form extends Component {
getFormValues = () => {
return this.fields.reduce((values, field) => {
const name = field.getName();
const fieldValue = field.getValue();
values[name] = fieldValue;
return values;
}, {});
};
}
}
}
/**
* Field組件
*/
class Field extends Component {
getValue = () => {
return this.state._value;
};
}表單驗證&錯誤提示
表單驗證是一個重頭戲,只有驗證通過了才能提交表單。驗證的時機也有多種,如字段變更時、鼠標移出時和表單提交時。ZentForm提供了一些常用的驗證規(guī)則,如非空驗證,長度驗證,郵箱地址驗證等。當然還能自定義一些更復雜的驗證方式。自定義驗證方法可以通過兩種方式傳入ZentForm,一種是通過給createForm傳參:
createForm({
formValidations: {
rule1(values, value){
},
rule2(values, value){
},
}
})(FormComp);另一種方式是給Field組件傳屬性:
<Field
validations={{
rule1(values, value){
},
rule2(values, value){
},
}}
validationErrors={{
rule1: 'error1',
rule2: 'error2'
}}
/>使用createForm傳參的方式,驗證規(guī)則是共享的,而Field的屬性傳參是字段專用的。validationErrors指定校驗失敗后的提示信息。這里的錯誤信息會顯示在前面getControlGroup所定義HTML中{showError && (<p className="zent-form__error-desc">{props.error}</p>)}
ZentForm的核心驗證邏輯是createForm的runRules方法,
runRules = (value, currentValues, validations = {}) => {
const results = {
errors: [],
failed: [],
};
function updateResults(validation, validationMethod) {
// validation方法可以直接返回錯誤信息,否則需要返回布爾值表明校驗是否成功
if (typeof validation === 'string') {
results.errors.push(validation);
results.failed.push(validationMethod);
} else if (!validation) {
results.failed.push(validationMethod);
}
}
Object.keys(validations).forEach(validationMethod => {
...
// 使用自定義校驗方法或內置校驗方法(可以按需添加)
if (typeof validations[validationMethod] === 'function') {
const validation = validations[validationMethod](
currentValues,
value
);
updateResults(validation, validationMethod);
} else {
const validation = validationRules[validationMethod](
currentValues,
value,
validations[validationMethod]
);
}
});
return results;
};默認的校驗時機是字段值改變的時候,可以通過Field的validateOnChange和validateOnBlur來改變校驗時機。
<Field
validateOnChange={false}
validateOnBlur={false}
validations={{
required: true,
matchRegex: /^[a-zA-Z]+$/
}}
validationErrors={{
required: '值不能為空',
matchRegex: '只能為字母'
}}
/>對應的,在Field組件中有2個方法來處理change和blur事件:
class Field extends Component {
handleChange = (event, options = { merge: false }) => {
...
this.setValue(newValue, validateOnChange);
...
}
handleBlur = (event, options = { merge: false }) => {
...
this.setValue(newValue, validateOnBlur);
...
}
setValue = (value, needValidate = true) => {
this.setState(
{
_value: value,
_isDirty: true,
},
() => {
needValidate && this.context.zentForm.validate(this);
}
);
};
}當觸發(fā)驗證的時候,ZentForm是會對表單對所有字段進行驗證,可以通過指定relatedFields來告訴表單哪些字段需要同步進行驗證。
表單提交
表單提交時,一般會經歷如下幾個步驟
表單驗證
表單提交
提交成功處理
提交失敗處理
ZentForm通過handleSubmit高階函數(shù)定義了上述幾個步驟,只需要傳入表單提交的邏輯即可:
const handleSubmit = (submit, zentForm) => {
const doSubmit = () => {
...
result = submit(values, zentForm);
...
return result.then(
submitResult => {
...
if (onSubmitSuccess) {
handleOnSubmitSuccess(submitResult);
}
return submitResult;
},
submitError => {
...
const error = handleSubmitError(submitError);
if (error || onSubmitFail) {
return error;
}
throw submitError;
}
);
}
const afterValidation = () => {
if (!zentForm.isValid()) {
...
if (onSubmitFail) {
handleOnSubmitError(new SubmissionError(validationErrors));
}
} else {
return doSubmit();
}
};
const allIsValidated = zentForm.fields.every(field => {
return field.props.validateOnChange || field.props.validateOnBlur;
});
if (allIsValidated) {
// 不存在沒有進行過同步校驗的field
afterValidation();
} else {
zentForm.validateForm(true, afterValidation);
}
}使用方式如下:
const { handleSubmit } = this.props;
<Form onSubmit={handleSubmit(this.submit)} horizontal>ZentForm不足之處
ZentForm雖然功能強大,但仍有一些待改進之處:
父組件維護了所有字段的實例,直接調用實例的方法來取值或者驗證。這種方式雖然簡便,但有違React聲明式編程和函數(shù)式編程的設計思想,并且容易產生副作用,在不經意間改變了字段的內部屬性。
大部分的組件重使用了shouldComponentUpdate,并對state和props進行了深比較,對性能有比較大的影響,可以考慮使用PureComponent。
太多的情況下對整個表單字段進行了校驗,比較合理的情況應該是某個字段修改的時候只校驗本身,在表單提交時再校驗所有的字段。
表單提交操作略顯繁瑣,還需要調用一次handleSubmit,不夠優(yōu)雅。
關于如何封裝React Form組件問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關知識。
文章名稱:如何封裝ReactForm組件
轉載源于:http://chinadenli.net/article26/iidhjg.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供面包屑導航、定制網(wǎng)站、網(wǎng)站改版、網(wǎng)站設計公司、關鍵詞優(yōu)化、營銷型網(wǎng)站建設
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)