是什么?
- 一个基于React组件概念的函数:接收一个
组件为参数,并返回一个新的组件的函数 - 一个复用组件逻辑的高级技巧
为什么?
-
可以增强代码的复用性和灵活性,提高开发效率
-
对高阶组件的理解有助于我们理解各种React第三方库
-
解决以下三个问题
- 抽取
重复代码,实现组件复用,常见场景:页面复用 - 条件渲染,控制组件的渲染逻辑(渲染劫持),常见场景:
权限控制 - 捕获/拦截生命周期,常见场景:组件渲染
性能追踪,日志打点
- 抽取
实现
属性代理
可以实现对接收的组件进行一些操作:
修改props
function HOC(WrapperComponent) {
return class extends React.component {
render() {
const newProps = { name: '333' }
<WrapperComponent {...this.props} { ...newProps }/>
}
}
}
像上面这样,组件永远都能接受到一个name属性的props
抽象state
function HOC(WrapperComponent) {
return class extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
}
handleCountChange = () => {
this.setState(prevState => {
return {
count: prevState.count + 1,
}
})
}
render() {
const newProps = {
count: this.state.count,
handleCountChange: this.handleCountChange.bind(this);
}
return (
<WrapperComponent {...props} {...newProps} />
)
}
}
}
上面的例子,就把接收到的所有WrapperComponent组件的count和handleCountChange给抽象出来了
操作refs
function HOC(WrapperComponent) {
return class extends React.Component {
componentRef = React.createRef();
componentDidMount() {
// 这里就拿到了WrapperComponent的实例方法
this.componentRef.current.focus();
}
render() {
return (<WrapperComponent ref={this.componentRef} {...this.props}/>)
}
}
}
通过props控制渲染条件
function HOC(WrapperComponent) {
return class extends React.Component {
render() {
if (this.props.hasRight) {
return <WrapperComponent {...this.props}/>
}
return <div>没有权限</div>
}
}
}
对接收的组件进行包裹
function HOC(WrapperComponent) {
return class extends React.Component {
render() {
return (
<div>
// 做花里胡哨的包装
<WrapperComponent/>
</div>
)
}
}
}
反向继承
最简单,且毫无意义的例子
function HOC(WrapperComponent){
return class extends WrapperComponent {
render() {
return super.render();
}
}
}
劫持生命周期和方法
function HOC(WrapperComponent){
return class extends WrapperComponent {
// 这里会重写这个方法
componentDidMount() {
// do something...
}
render() {
return super.render();
}
}
}
读取/操作state
function HOC(WrapperComponent) {
return class extends WrapperComponent {
componentDidMount() {
console.info(this.state) // 获取state
this.setState({ name: 12 }) // 修改
}
}
}
实践
页面复用
这个也是最常见的例子,如果两个页面,有很多共同的方法、数据
// pageA
class PageA extends React.Component {
state = {
list: []
}
async componentDidMount() {
const newList = await getData("hanguo")
this.setState({ list: newList })
}
render() {
return <List list={this.state.list}/>
}
}
// pageB
class PageB extends React.Component {
state = {
list: []
}
async componentDidMount() {
const newList = await getData("rimei")
this.setState({ list: newList })
}
render() {
return <List list={this.state.list}/>
}
}
从上面例子可以看到,PageA和PageB很多地方都可以公用,唯一的区别就是getData接收的参数不同
function HOC(WrapperComponent, type) {
return class extends React.Component {
state = {
list: []
}
async componentDidMount() {
const newList = await getData(type)
this.setState({ list: newList })
}
render() {
return <WrapperComponent list={this.state.list}/>
}
}
}
// pageA.js
HOC(List, "hanguo")
// pageB.js
HOC(List, "rimei")
如此便设计了一个高阶组件,完成了组件的公共方法的提取和扩展
权限控制
import React from 'react';
import { whiteListAuth } from '../lib/utils'; // 鉴权方法
/**
* 白名单权限校验
* @param WrappedComponent
* @returns {AuthWrappedComponent}
* @constructor
*/
function AuthWrapper(WrappedComponent) {
return class AuthWrappedComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
permissionDenied: -1,
};
}
async componentDidMount() {
try {
await whiteListAuth(); // 请求鉴权接口
this.setState({
permissionDenied: 0,
});
} catch (err) {
this.setState({
permissionDenied: 1,
});
}
}
render() {
if (this.state.permissionDenied === -1) {
return null; // 鉴权接口请求未完成
}
if (this.state.permissionDenied) {
return <div>功能即将上线,敬请期待~</div>;
}
return <WrappedComponent {...this.props} />;
}
}
}
export default AuthWrapper;
组件渲染性能追踪
利用反向继承,在生命周期中加入数据,监控渲染完成时间
function RenderTime(WrapperComponent) {
let start, end = 0;
return class extends WrapperComponent {
componentWillMount() {
super.componentWillMount();
start = Date.now();
}
componentDidMount() {
super.componentDidMount();
end = Date.now();
console.info(本次渲染完成时间, end - start) // 伪代码
}
render() {
return super.render();
}
}
}
总结
-
高阶组件不是组件,它是一个将某个组件转换成另一个组件的纯函数。
-
高阶组件的主要作用是实现代码复用和逻辑抽象、对
state和props进行抽象和操作、对组件进行细化(如添加生命周期)、实现渲染劫持等。在实际的业务场景中合理的使用高阶组件,可以提高开发效率和提升代码的可维护性。 -
高阶组件的实用性 💪使其频繁地被大量
React.js相关的第三方库,如React-Redux的connect方法、React-Loadable等所使用,了解高阶组件对我们理解各种React.js第三方库的原理很有帮助 👍。 -
高阶组件有两种实现方式,分别是属性代理和反向继承。它可以看作是装饰器模式在
React中的实现:在不修改原组件的情况下实现组件功能的增强。