Higher Order Components(HOC) 的本質是Higher Order Functions,只不過返回值不是普通函數,而是React的component。利用HOC咱們能夠把類似的業務邏輯抽離出來,而後組件只負責渲染視圖和組件相關的業務邏輯,而公用的業務邏輯則使用HOC中定義好的公共邏輯,以實現代碼複用。本文將用兩個簡單的Form組件來演示HOC的基本使用方法。javascript
首先咱們來建立一個未使用HOC的Form組件:react
import React from "react";
class PersonalInfoForm extends React.Component {
state = {
name: "",
age: 0
};
handleInputChange = event => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
};
handleSubmit = event => {
event.preventDefault();
console.log(this.state);
};
render() {
return (
<form>
<div>
<label>
Name:
<input
type="text"
value={this.state.name}
name="name"
onChange={this.handleInputChange}
/>
</label>
</div>
<div>
<label>
Age:
<input
type="number"
value={this.state.age}
name="age"
onChange={this.handleInputChange}
/>
</label>
</div>
<div>
<button onClick={this.handleSubmit}>Submit</button>
</div>
</form>
);
}
}
export default PersonalInfoForm;
複製代碼
這裏咱們定義了姓名和年齡輸入框和提交按鈕,而後處理輸入框值變化的回調函數handleInputChange
以及表單提交處理回調handleSubmit()
。app
如今看來這個組件彷佛沒有什麼問題,可是當咱們的業務邏輯複雜起來,愈來愈多的Form組件加入進來以後,咱們要爲每一個Form組件編寫onChange和onSubmit的回調函數,以及表單驗證處理。那麼這時候咱們就能夠定義一個HOC組件來處理這些冗餘的代碼,實現一處定義多處使用。函數
新建一個Form.js
文件,在裏邊定義form()
函數,代碼以下:佈局
import React from "react";
export default function form(fields) {
return WrappedForm => {
return class extends React.Component {
state = { ...fields };
handleInputChange = event => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
};
handleSubmit = (event, onSubmit) => {
event.preventDefault();
// do any default validations and other stuff
onSubmit();
};
render() {
return (
<WrappedForm onChange={this.handleInputChange} onSubmit={this.handleSubmit} fields={{ ...this.state }} {...this.props} /> ); } }; }; } 複製代碼
form()
函數接收一個fields參數,用來保存被包裹的組件的表單項,而後返回了一個React Component,在這個組件中定義了state,初始值爲被包裹組件傳遞進來的fields的值,還定義了handleInputChange和handleSubmit方法。這裏handleSubmit中能夠定義一些諸如表單驗證邏輯,而後它接收一個函數,即被包裹組件的特定的表單處理邏輯,好比發送請求到後臺API,或者更新某個組件狀態等。測試
而後在render()中咱們直接渲染了被包裹的組件的樣式,而後把handleInputChange、handleSubmit、當前的state、還有pass through props(即HOC不須要,可是被包裹的組件須要的props)傳遞給了被包裹的組件。ui
讓咱們新建一個<PersonalInfoFormWithHOC>
組件,代碼以下:this
import React from "react";
import form from "./Form";
function PersonalInfoFormWithHOC({ onChange, onSubmit, fields }) {
const handleSubmit = event => {
onSubmit(event, () => {
console.log(fields);
});
};
return (
<form>
<div>
<label>
Name:
<input
type="text"
value={fields.name}
name="name"
onChange={onChange}
/>
</label>
</div>
<div>
<label>
Age:
<input
type="number"
value={fields.age}
name="age"
onChange={onChange}
/>
</label>
</div>
<div>
<button onClick={handleSubmit}>Submit</button>
</div>
</form>
);
}
export default form({ name: "", age: 0 })(PersonalInfoFormWithHOC);
複製代碼
由於有了HOC,咱們再也不須要state,因此這裏定義成了函數式組件(functional component),而後經過HOC傳遞進來的props來處理表單數據。咱們在最後export的時候調用了form
HOC,把初始的表單項的值傳遞給了它,而咱們可以在HOC傳遞回來的fields prop來動態的更新input的value。最後咱們定義了組件特定的處理表單提交的邏輯,這裏只爲演示用,就打印了用戶填寫的表單值。spa
而後設想又有一個新的表單組件<EmployeeForm>
加入進來,代碼以下:
import React from "react";
import form from "./Form";
function EmployeeForm({ onChange, onSubmit, fields }) {
const handleSubmit = event => {
onSubmit(event, () => {
console.log(fields);
});
};
return (
<form>
<div>
<label>
Name:
<input
type="text"
value={fields.name}
name="name"
onChange={onChange}
/>
</label>
</div>
<div>
<label>
Position:
<input
type="text"
value={fields.position}
name="position"
onChange={onChange}
/>
</label>
</div>
<div>
<label>
Marital Status:
<select name="mstatus" value={fields.mstatus} onChange={onChange}>
<option value="">select</option>
<option value="single">Single</option>
<option value="married">Married</option>
</select>
</label>
</div>
<div>
<button onClick={handleSubmit}>Submit</button>
</div>
</form>
);
}
export default form({ name: "", position: "", mstatus: "" })(EmployeeForm);
複製代碼
這裏面咱們只需定義特定的表單處理邏輯便可。而再新建form組件時,咱們只關心如何佈局表單項和表單處理邏輯便可。最後在index.js中引用這兩個組件便可進行測試:
function App() {
return (
<div className="App"> {/*<PersonalInfoForm /> */} <h1>Personal Info Form</h1> <PersonalInfoFormWithHOC /> <h1>Employee Form</h1> <EmployeeForm /> </div>
);
}
複製代碼