如今咱们都知道高阶函数是什么,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