React-HOC瞭解一下

北宸南蓁發表於2018-09-02

用技術改變生活,用生活完善技術。來自攜程(旅悅)一枚向全棧方向努力奔跑的前端工程師。 微信同步:wDxKn89

最近在公司接了一個老專案遷移React。在元件開發過程中,發現有一些元件的處理邏輯很類似。想在某一個地方來統一處理。進過思考和分析,發現HOC(higher-order component)很適合完成此類工作。 再次來總結HOC的概念和基本用法。


HOC本質

針對React開發來講,component是頁面的基本組成單位。一個頁面可以由很多擁有各自處理邏輯的元件來組合。正如在開發工程中遇到的問題,某幾個元件擁有類似的資料過程,是不是可以採用某一種機制或者方法來統一處理該資料處理。

所以HOC就出現了。

a higher-order component is a function that takes a component and returns a new component.

注意:HOC是一個function而這個函式接受一個元件,return被處理過的元件。

const EnhancedComponent = higherOrderComponent(WrappedComponent);
複製程式碼

Code實現

案例重現

現在有一個需求,現在有TestTableA,TestTableB,他們用於渲染table資料,同時接收的資料格式也類似。只是TestTableA比TestTableB多一列展示資料。

col1 col2 col3
內容 內容 內容
內容 內容 內容

col1 col2
內容 內容
內容 內容

程式碼實現

現在採用HOC來統一處理table colum的拼裝和資料的獲取。(元件是用的Antd)

ReferencePriceFactory (HOC元件構建)

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;
複製程式碼

TestTableA/TestTableB程式碼實現

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>
	)
	}
}
複製程式碼

在需要用到該元件的地方進行元件的處理,並呼叫。

HOC使用注意事項

不能修改原始元件

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來對該元件進行加強(修改的是同一個方法),最後一次修改會對上一次加強的結果進行重寫。

通過直接修改元件的方法,是弱抽象的。 如果想規避上述問題,構建一個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的本質就是一個用於返回被加強的元件的函式。所以如果一個元件需要進行不同維度的加強。就需要對元件進行多次HOC處理。

const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent))
複製程式碼

但是如果對函數語言程式設計有了解的話,就會對compose有過了解。 通過compose處理上述程式碼可以改寫如下:

const enhance = compose(
  withRouter,
  connect(commentSelector)
)
const EnhancedComponent = enhance(WrappedComponent)
複製程式碼

Note

HOC是不能夠在render方法中使用的。

React的diff演算法通過判斷component ID來決定是否更新存在的子樹或者刪除子樹,並且重新載入一個新的。如果從render方法中返回的元件(===)原來的渲染的元件。但是HOC是一個函式,每次呼叫都會返回一個新的元件,所以render方法每次呼叫都會觸發diff處理,並將原有的元件進行刪除,重新載入一個新的元件。

元件的靜態方法必須在HOC中重新呼叫

當你應用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';
複製程式碼

相關文章