高阶函数(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,以防止混淆。