基本概念
高阶函数官网介绍:
1 2
| 高阶组件是参数为组件,返回值为新组件的函数。 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。
|
所以高阶组件是一个函数而不是一个组件,它的作用是将组件转化为另一个组件。
使用高阶函数可以解决以下三个方面的问题:
- 抽象出重复代码,实现组件复用
- 控制组件的渲染逻辑,场景:权限控制
- 劫持被处理组件的生命周期,场景:组件渲染性能追踪
实现
通常实现高阶组件的有两种方式:
属性代理
- 返回一个无状态的函数组件
- 返回一个有状态的class组件
反向继承
使用属性代理
操作props
使用HOC实现简单的属性代理,具体实现见 demo10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function HOC(WrappedComponent) { const newProps = { type: 'HOC' }; return props => <WrappedComponent {...props} {...newProps}/>; }
function HOC(WrappedComponent) { return class extends React.Component { render() { const newProps = { type: 'HOC' }; return <WrappedComponent {...this.props} {...newProps}/>; } }; }
|
拦截父组件传递的props
,添加type
属性
抽象state
属性代理是无法直接操作原组件的state,但可以通过props
和回调函数对state
抽象。具体实现见
demo11
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 26 27 28 29 30 31 32 33 34 35 36 37 38
| function HOC(WrappedComponent) { return class extends React.Component { constructor(props) { super(props); this.state = { name: '', }; this.onChange = this.onChange.bind(this); } onChange = (event) => { this.setState({ name: event.target.value, }) } render() { const newProps = { name: { value: this.state.name, onChange: this.onChange, }, }; return <WrappedComponent {...this.props} {...newProps} />; } }; } class Example extends React.Component { render() { return ( <div> <input name="name" {...this.props.name} /> {this.props.name.value} </div> ) } }
|
通过newProps来获取state中的值,并且调用onChange事件来实现更新state中的数据。
获取refs引用
使用ref
属性可以访问到DOM(focus事件、动画、第三方DOM操作库)。ref
属性只能声明在class类型的组件上,函数类型的组件无法声明,因为无状态组件没有实例。
同样,直接通过属性代理是无法获取原组件的refs引用,可以通过在原组件的ref
回调函数中调用父组件传入的 ref
回调函数来获取原组件的refs
引用。官网也给出一个实现方法是通过使用React.forwardRef
API。
获取static
当待处理的组件是class组件时,通过属性代理实现的高阶组件可以获取原组件的static方法。具体实现见具体实现见
demo12
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 26 27 28 29 30 31 32 33
| function HOC(WrappedComponent) { function wrappedComponentStaic () { WrappedComponent.sayHello(); } return (props) => ( <div> <WrappedComponent {...props} /> <input type="button" value="调用子组件static" onClick={wrappedComponentStaic} /> </div> ); } // 使用 class Example extends React.Component { static sayHello () { console.error('hello world'); } render() { return ( <div> <input name="name" /> </div> ) } }
|
props实现条件渲染
通过 props
来控制是否渲染及传入数据,利用三目运算判断
反向继承
反向继承是指使用一个函数接收一个组件作为参数传入,并返回一个继承了传入组件的类组件,且在返回组件的render()中返回super.render()
1 2 3 4 5 6 7 8
| 简单实现 const HOC = (WrappedComponent) => { return class extends WrappedComponent { render() { return super.render(); } } }
|
它的特点是允许高阶组件通过this访问原组件,所以可以直接读取和操作原组件的state/ref/生命周期方法
注意事项
不要在render中使用HOC
重新挂载组件会导致组件及其所有子组件的状态丢失,如果在组件之外创建 HOC,这样一来组件只会创建一次
1 2 3 4 5 6 7
| render() { // 每次调用 render 函数都会创建一个新的 EnhancedComponent // EnhancedComponent1 !== EnhancedComponent2 const EnhancedComponent = enhance(MyComponent); // 这将导致子树每次渲染都会进行卸载,和重新挂载的操作! return <EnhancedComponent />; }
|
refs不会被传递
因为ref不是一个prop,它是由react专门处理的,如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。