用技術改變生活,用生活完善技術。來自攜程(旅悅)一枚向全棧方向努力奔跑的前端工程師。 微信同步:wDxKn89html
最近在公司接了一個老項目遷移React。在組件開發過程當中,發現有一些組件的處理邏輯很相似。想在某一個地方來統一處理。進過思考和分析,發現HOC(higher-order component)很適合完成此類工做。 再次來總結HOC的概念和基本用法。前端
針對React開發來說,component是頁面的基本組成單位。一個頁面能夠由不少擁有各自處理邏輯的組件來組合。正如在開發工程中遇到的問題,某幾個組件擁有相似的數據過程,是否是能夠採用某一種機制或者方法來統一處理該數據處理。react
因此HOC就出現了。算法
a higher-order component is a function that takes a component and returns a new component.編程
注意:HOC是一個function而這個函數接受一個組件,return被處理過的新組件。segmentfault
const EnhancedComponent = higherOrderComponent(WrappedComponent);
複製代碼
如今有一個需求,如今有TestTableA,TestTableB,他們用於渲染table數據,同時接收的數據格式也相似。只是TestTableA比TestTableB多一列展現數據。bash
col1 | col2 | col3 |
---|---|---|
內容 | 內容 | 內容 |
內容 | 內容 | 內容 |
col1 | col2 |
---|---|
內容 | 內容 |
內容 | 內容 |
如今採用HOC來統一處理table colum的拼裝和數據的獲取。(組件是用的Antd)微信
import React from 'react';
function ReferencePriceFactory(WrappedComponent,configInfo) {
return class extends React.Component {
constructor(props){
super(props);
}
render() {
//這裏爲了方便,直接假設用調用處將數據傳入
const {tableData} = this.props;
renderContent
const renderContent = (text, row, index) => {
let value;
const obj = {
children: {},
props: {}
}
value = <span>{text}</span>
obj.children = value;
return obj;
}
let Columns = [{
title:'col1' ,
dataIndex:'type',
render: (text,record,index) =>renderContent(text,record,index)
}, {
title: 'col2',
dataIndex:'referencePrice',
render: (text,record,index) =>renderContent(text,record,index)
}];
let empty = {
colSpan:0,
render: (text, record, index) => {
let value = null;
const obj = {
children: value,
props: {},
};
obj.props.colSpan = 0;
return obj;
},
};
let extraColumns =configInfo? {
title: col3,
dataIndex:'playPrice',
render: (text,record,index) =>renderContent(text,record,index)
}:empty;
const finalColumns = [...Columns,extraColumns];
return <WrappedComponent {...this.props} finalColumns={finalColumns}/>;
}
}
}
export default ReferencePriceFactory;
複製代碼
import React from 'react';
import { Table} from 'antd';
export default class TestTableA extends React.Component {
constructor(props, context) {
super(props, context);
}
render() {
let {finalColumns,tableData} = this.props;
return (
<span>
<Table dataSource={tableData} columns={finalColumns} pagination={false} />
</span>
)
}
}
複製代碼
const EnhancedTestTableA = ReferencePriceFactory(TestTableA,true);
const EnhancedTestTableB = ReferencePriceFactory(TestTableB,true);
export default class Test extends React.Component {
constructor(props, context) {
super(props, context);
}
render() {
const {record} = this.props;
return (
<span>
<EnhancedTestTableA record={record}/>
<EnhancedTestTableB record={record}/>
</span>
)
}
}
複製代碼
在須要用到該組件的地方進行組件的處理,並調用。前端工程師
function logProps(InputComponent) {
//經過修改prototype來修改組件
InputComponent.prototype.componentWillReceiveProps = function(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
};
//此處其實已經修改了InputComponent了
return InputComponent;
}
// 組件調用
const EnhancedComponent = logProps(InputComponent);
複製代碼
經過該方式來處理組件,最大的壞處就是若是還有另一個HOC來對該組件進行增強(修改的是同一個方法),最後一次修改會對上一次增強的結果進行重寫。antd
經過直接修改組件的方法,是弱抽象的。 若是想規避上述問題,構建一個HOC來增強組件是經過構建一個組件來調用須要被增強的組件。
function logProps(WrappedComponent) {
return class extends React.Component {
componentWillReceiveProps(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
}
複製代碼
經過如上的分析會發現HOC和Container component處理方式很相似。
經過上文分析,HOC的本質就是一個用於返回被增強的組件的函數。因此若是一個組件須要進行不一樣維度的增強。就須要對組件進行屢次HOC處理。
const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent))
複製代碼
可是若是對函數式編程有了解的話,就會對compose有過了解。 經過compose處理上述代碼能夠改寫以下:
const enhance = compose(
withRouter,
connect(commentSelector)
)
const EnhancedComponent = enhance(WrappedComponent)
複製代碼
React的diff算法經過判斷component ID來決定是否更新存在的子樹或者刪除子樹,而且從新加載一個新的。若是從render方法中返回的組件(===)原來的渲染的組件。可是HOC是一個函數,每次調用都會返回一個新的組件,因此render方法每次調用都會觸發diff處理,並將原有的組件進行刪除,從新加載一個新的組件。
當你應用HOC經過調用原始組件經過增強來返回一個新的組件。這意味着新的組件沒有原始組件的任何靜態方法,因此想要在新的組件中使用靜態方法的話,就須要在HOC中進行從新調用。
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}
複製代碼
或者經過將靜態方法export出來,在調用出import
MyComponent.someFunction = someFunction;
export default MyComponent;
//單獨導出
export { someFunction };
import MyComponent, { someFunction } from './MyComponent.js';
複製代碼