React-HOC瞭解一下

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

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


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);
複製代碼

Code實現

案例重現

如今有一個需求,如今有TestTableA,TestTableB,他們用於渲染table數據,同時接收的數據格式也相似。只是TestTableA比TestTableB多一列展現數據。bash

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來對該組件進行增強(修改的是同一個方法),最後一次修改會對上一次增強的結果進行重寫。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的本質就是一個用於返回被增強的組件的函數。因此若是一個組件須要進行不一樣維度的增強。就須要對組件進行屢次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';
複製代碼
相關文章
相關標籤/搜索