如今咱們都知道高階函數是什麼,Higher-Ordercomponents(高階組件)其實也是相似於高階函數,它接受一個React組件做爲輸入,輸出一個新的React組件html
Concretely, a higher-order component is a function that takes a component and returns a new component.react
通俗的語言解釋:當咱們用一個容器(w)把React組件包裹,高階組件會返回一個加強(E)的組件。高階組件讓咱們的代碼更具備複用性,邏輯性與抽象特。它能夠對props和state進行控制,也能夠對render方法進行劫持...編程
大概是這樣:redux
const EnhancedComponent = higherOrderComponent(WrappedComponent)
api
簡單例子:bash
import React, { Component } from 'react';
import ExampleHoc from './example-hoc';
class UseContent extends Component {
render() {
console.log('props:',this.props);
return (
<div>
{this.props.title} - {this.props.name}
</div>
)
}
}
export default ExampleHoc(UseContent)
複製代碼
import React, { Component } from 'react';
const ExampleHoc = WrappedComponent => {
return class extends Component {
constructor(props) {
super(props)
this.state = {
title: 'hoc-component',
name: 'arcsin1',
}
}
render() {
const newProps = {
...this.state,
}
return <WrappedComponent {...this.props} {...this.newProps} />
}
}
}
export default ExampleHoc
複製代碼
組件UseContent,你能夠看到實際上是一個很簡單的一個渲染而已,而組件ExampleHoc對它進行了加強,很簡單的功能.app
如下代碼我會用裝飾器(decorator)書寫函數式編程
小列子說明:函數
import React, { Component } from 'react'
import ExampleHoc from './example-hoc'
@ExampleHoc
export default class UseContent extends Component {
render() {
console.log('props:',this.props);
return (
<div>
{...this.props} //這裏只是演示
</div>
)
}
}
複製代碼
import React, { Component } from 'react'
const ExampleHoc = WrappedComponent => {
return class extends Component {
render() {
return <WrappedComponent {...this.props} />
}
}
}
export default ExampleHoc
複製代碼
這樣的組件就能夠做爲參數被調用,原始組件就具有了高階組件對它的修飾。就這麼簡單,保持單個組件封裝性的同時還保留了易用性。固然上述的生命週期以下:工具
didmount -> HOC didmount ->(HOCs didmount) ->(HOCs willunmount)-> HOC willunmount -> unmount
控制props
我能夠讀取,編輯,增長,移除從WrappedComponent傳來的props,可是須要當心編輯和移除props。咱們應該對高階組件的props做新的命名防止混淆了。
例如:
import React, { Component } from 'react'
const ExampleHoc = WrappedComponent => {
return class extends Component {
render() {
const newProps = {
name: newText,
}
return <WrappedComponent {...this.props} {...newProps}/>
}
}
}
export default ExampleHoc
複製代碼
經過refs使用引用
在高階組件中,咱們能夠接受refs使用WrappedComponent的引用。 例如:
import React, { Component } from 'react'
const ExampleHoc = WrappedComponent => {
return class extends Component {
proc = wrappedComponentInstance => {
wrappedComponentInstance.method()
}
render() {
const newProps = Object.assign({}, this.props,{
ref: this.proc,
})
return <WrappedComponent {...this.newProps} />
}
}
}
export default ExampleHoc
複製代碼
當WrappedComponent被渲染的時候,refs回調函數就會被執行,這樣就會拿到一份WrappedComponent的實例的引用。這樣就能夠方便地用於讀取和增長實例props,並調用實例。
咱們能夠經過WrappedComponent提供props和回調函數抽象state。就像咱們開始的例子,咱們能夠把原組件抽象爲展現型組件,分離內部狀態,搞成無狀態組件。
例子:
import React, { Component } from 'react';
const ExampleHoc = WrappedComponent => {
return class extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
}
}
onNameChange = e => {
this.setState({
name: e.target.value,
})
}
render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onNameChange,
}
}
return <WrappedComponent {...this.props} {...newProps} />
}
}
}
export default ExampleHoc
複製代碼
在上面 咱們經過把input的name prop和onchange方法提到了高階組件中,這樣有效的抽象了一樣的state操做。
用法:
import React, { Component } from 'react'
import ExampleHoc from './example-hoc'
@ExampleHoc
export default class UseContent extends Component {
render() {
console.log('props:',this.props);
return (
<input name="name" {this.props.name} />
)
}
}
這樣就是一個受控組件
複製代碼
其它元素包裹WrappedComponent
其它,咱們可使用其它元素包裹WrappedComponent,這樣既能夠增長樣式,也能夠方便佈局。例如
import React, { Component } from 'react'
const ExampleHoc = WrappedComponent => {
return class extends Component {
render() {
return (
<div style={{display: 'flex'}}>
<WrappedComponent {...this.props} />
</div>
)
}
}
}
export default ExampleHoc
複製代碼
從字面意思,能夠看出它與繼承相關,先看看例子:
const ExampleHoc = WrappedComponent => {
return class extends WrappedComponent {
render() {
return super.render()
}
}
}
複製代碼
正如看見的,高階組件返回的組件繼承與WrappedComponent,由於被動繼承了WrappedComponent,全部的調用都是反向。因此這就是反代繼承的由來。 這種方法與屬性代理不太同樣,它經過繼承WrappedComponent來實現,方法能夠經過super來順序調用,來看看生命週期:
didmount -> HOC didmount ->(HOCs didmount) -> willunmount -> HOC willunmount ->(HOCs willunmount)
在反向繼承中,高階函數可使用WrappedComponent的引用,這意味着可使用WrappedComponent的state,props,生命週期和render方法。但它並不能保證完整的子組件樹被解析,得注意。
渲染劫持就是高階組件能夠控制WrappedComponent的渲染過程,並渲染各類各樣的結果。咱們能夠在這個過程當中任何React元素的結果中讀取,增長,修改,刪除props,或者修改React的元素樹,又或者用樣式控制包裹這個React元素樹。
由於前面說了它並不能保證完整的子組件樹被解析,有個說法:咱們能夠操控WrappedComponent元素樹,並輸出正確結果,但若是元素樹種包含了函數類型的React組件,就不能操做組件的子組件。
先看看有條件的渲染例子:
const ExampleHoc = WrappedComponent => {
return class extends WrappedComponent {
render() {
if(this.props.loggedIn) { //當登陸了就渲染
return super.render()
} else {
return null
}
}
}
}
複製代碼
對render輸出結果的修改:
const ExampleHoc = WrappedComponent => {
return class extends WrappedComponent {
render() {
const eleTree = super.render()
let newProps = {}
if(eleTree && eleTree.type === 'input') {
newProps = {value: '這不能被渲染'}
}
const props = Object.assgin({},eleTree.props,newProps)
const newEleTree = React.cloneElement(eleTree, props, eleTree.props.children)
return newEleTree
}
}
}
複製代碼
控制state
高階組件是能夠讀取,修改,刪除WrappedComponent實例的state,若是須要的話,也能夠增長state,但這樣你的WrappedComponent會變得一團糟。所以大部分的高階組件多都應該限制讀取或者增長state,尤爲是增長state,能夠經過從新命名state,以防止混淆。
看看例子:
const ExampleHoc = WrappedComponent => {
return class extends WrappedComponent {
render() {
<div>
<h3>HOC debugger</h3>
<p>Props <pre>{JSON.stringfy(this.props,null,1)}</pre></p>
<p>State <pre>{JSON.stringfy(this.state,null,2)}</pre></p>
{super.render()}
</div>
}
}
}
複製代碼
舉個列子,我把用戶信息存在本地LocalStorage中,固然裏面有不少key,可是我不須要用到全部,我但願按照個人喜愛獲得我本身想要的。
import React, { Component } from 'react'
const ExampleHoc = (key) => (WrappedComponent) => {
return class extends Component {
componentWillMount() {
let data = localStorage.getItem(key);
this.setState({data});
}
render() {
return <WrappedComponent data={this.state.data} {...this.props} />
}
}
}
複製代碼
import React, { Component } from 'react'
class MyComponent2 extends Component {
render() {
return <div>{this.props.data}</div>
}
}
const MyComponent2WithHOC = ExampleHoc(MyComponent2, 'data')
export default MyComponent2WithHOC
複製代碼
import React, { Component } from 'react'
class MyComponent3 extends Component {
render() {
return <div>{this.props.data}</div>
}
}
const MyComponent3WithHOC = ExampleHoc(MyComponent3, 'name')
export default MyComponent3WithHOC
複製代碼
實際上,此時的ExampleHoc和咱們最初對高階組件的定義已經不一樣。它已經變成了一個高階函數,但這個高階函數的返回值是一個高階組件。咱們能夠把它當作高階組件的變種形式。這種形式的高階組件大量出如今第三方庫中。如react-redux中的connect就是一個典型。請去查看react-redux的api就能夠知道了。
有問題望指出,謝謝!
參考:
Higher-Order Components: higher-order-components
React Higher Order Components in depth: React Higher Order Components in depth