高阶函数(higher-order function): 接受函数作为输入,或是输出一个函数。比如,常用的工具方法 map、reduce 和 sort 等都是高阶函数。
高阶组件(higher-order component): 类似于高阶函数,它接受 React 组件作为输入,输出一个新的 React 组件。

实现高阶组件的方法有如下两种:

  • 属性代理(props proxy)。高阶组件通过被包裹的 React 组件来操作 props。
  • 反向继承(inheritance inversion)。高阶组件继承于被包裹的 React 组件。

属性代理

1
2
3
4
5
6
7
import React, { Component } from 'react'
const MyContainer = (WrappedComponent) =>
class extends Component {
render() {
return <WrappedComponent {...this.props}>
}
}

控制props

1
2
3
4
5
6
7
8
9
10
import React, { Component } from 'react'
const MyContainer = (WrappedComponent) =>
class extends Component {
render() {
const newProps = {
text: newText,
}
return <WrappedComponent {...this.props} {...newProps} />
}
}
  • 对于原组件来说,只要套用这个高阶组件,我们的新组件中就会多一个 text 的 prop。

通过refs使用引用(思考使用方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
import React, { Component } from 'React';
const MyContainer = (WrappedComponent) =>
class extends Component {
proc(wrappedComponentInstance) {
wrappedComponentInstance.method();
}
render() {
const props = Object.assign({}, this.props, {
ref: this.proc.bind(this),
});
return <WrappedComponent {...props} />;
}
}
  • 当 WrappedComponent 被渲染时,refs 回调函数就会被执行,这样就会拿到一份Wrapped-
    Component 实例的引用。这就可以方便地用于读取或增加实例的 props,并调用实例的方法。

抽象state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { Component } from 'React';
const MyContainer = (WrappedComponent) =>
class extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
};
this.onNameChange = this.onNameChange.bind(this);
}
onNameChange(event) {
this.setState({
name: event.target.value,
})
}
render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onNameChange,
},
}
return <WrappedComponent {...this.props} {...newProps} />;
}
}
  • 高阶组件可以将原组件抽象为展示型组件,分离内部状态。

使用其他元素包裹WrappedComponent

1
2
3
4
5
6
7
8
9
10
11
import React, { Component } from 'React';
const MyContainer = (WrappedComponent) =>
class extends Component {
render() {
return (
<div style={{display: 'block'}}>
<WrappedComponent {...this.props} />
</div>
)
}
}
  • 为了加样式或者为了布局

反向继承

1
2
3
4
5
6
const MyContainer = (WrappedComponent) =>
class extends WrappedComponent {
render() {
return super.render();
}
}
  • 高阶组件返回的组件继承于 WrappedComponent。
  • 因为被动地继承了 WrappedComponent,所有的调用都会反向,这也是这种方法的由来。
  • 在反向继承方法中,高阶组件可以使用 WrappedComponent 引用,这意味着它可以使用WrappedComponent 的 state、props 、生命周期和 render 方法。但它不能保证完整的子组件树被解析。

渲染劫持

1
2
3
4
5
6
7
8
9
10
const MyContainer = (WrappedComponent) =>
class extends WrappedComponent {
render() {
if (this.props.loggedIn) {
return super.render()
} else {
return null
}
}
}
  • this.props.loggedIn
  • super.render()获取WrappedComponent的elementsTree
1
2
3
4
5
6
7
8
9
10
11
12
13
const MyContainer = (WrappedComponent) =>
class extends WrappedComponent {
render() {
const elementsTree = super.render()
let newProps = {}
if (elementsTree && elementsTree.type === 'input') {
newProps = { value: 'may the force be with you' }
}
const props = Object.assign({}, elementsTree.props, newProps);
const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children);
return newElementsTree;
}
}
  • React.cloneElement(element, props, children)
  • 反转元素树,改变元素树中的props

控制state

1
2
3
4
5
6
7
8
9
10
11
12
13
const MyContainer = (WrappedComponent) =>
class extends WrappedComponent {
render() {
return (
<div>
<h2>HOC Debugger Component</h2>
<p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>
<p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>
{super.render()}
</div>
);
}
}
  • 高阶组件可以读取、修改或删除 WrappedComponent 实例中的 state
  • 大部分的高阶组件都应该限制读取或增加 state,尤其是后者,可以通过重新命名 state,以防止混淆。