高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
具体而言,高阶组件是参数为组件,返回值为新组件的函数。
类似于Vue的mixin
下面以一个小例子简单说明HOC的用法
中文官网地址:https://react.docschina.org/docs/higher-order-components.html
1.首先创建一个Hoc.js文件,并编写相关公共的逻辑
import React, { Component } from 'react'
const Hoc = (WrappedComponent) => {
return class extends Component {
constructor(props) {
super(props)
this.state = {
//定义可复用的状态
count: 1,
}
}
componentDidMount() {
// 别的组件用HOC的话,先加载HOC的生命周期钩子函数
console.log('111')
}
//定义公共可复用的方法
getCode(num) {
this.setState({ count: num })
console.log(num)
}
render() {
console.log(this.props)
return (
<div>
<WrappedComponent getCode={this.getCode.bind(this)} state={this.state} />
</div>
)
}
}
}
export default Hoc
2.然后在其他需要用到公共方法或者数据中引入HOC并传入该组件本身为参数
import React, { Component } from 'react';
//引入HOC文件
import Hoc from './Hoc';
class MyComponentextends Component { constructor(props) {
super(props);
this.state = {
num: 1
};
}
render() {
console.log('----->', this.props);
return (
<div>
<button
onClick={() => { this.setState(
(state) => {
return { num: ++state.num };
},
() => {
// 使用高阶组件里复用的方法 触发Hoc文件的方法并传入相关的参数
this.props.getCode(this.state.num);
}
);
}}
>
</button> //获取HOC的值
<h1 style={{color: 'red'}}>{this.props.state.count}</h1> );
}
}
//MyComponent当做参数传递给HOC函数
export default Hoc(MyComponent);
按照上面的步骤,已经初步实现HOC的大体功能
注意点
1. 不要改变原始组件,使用组合。
不要试图在 HOC 中修改组件原型(或以其他方式改变它),通过将组件包装在容器组件中实现功能
function logProps(WrappedComponent) {
return class extends React.Component {
componentDidUpdate(prevProps) {
console.log('Current props: ', this.props);
console.log('Previous props: ', prevProps);
}
render() {
// 将 input 组件包装在容器中,而不对其进行修改。Good!
return <WrappedComponent {...this.props} />;
}
}
}
2.约定:将不相关的 props 传递给被包裹的组件
HOC 为组件添加特性。自身不应该大幅改变约定。HOC 返回的组件与原组件应保持类似的接口。
HOC 应该透传与自身无关的 props。大多数 HOC 都应该包含一个类似于下面的 render 方法:这种约定保证了 HOC 的灵活性以及可复用性。
render() {
// 过滤掉非此 HOC 额外的 props,且不要进行透传
const { extraProp, ...passThroughProps } = this.props;
// 将 props 注入到被包装的组件中。
// 通常为 state 的值或者实例方法。
const injectedProp = someStateOrInstanceMethod;
// 将 props 传递给被包装组件
return (
<WrappedComponent
injectedProp={injectedProp}
{...passThroughProps}
/>
);
}
3.不要在 render 方法中使用 HOC
React 的 diff 算法(称为协调)使用组件标识来确定它是应该更新现有子树还是将其丢弃并挂载新子树。 如果从 render 返回的组件与前一个渲染中的组件相同(===),则 React 通过将子树与新子树进行区分来递归更新子树。 如果它们不相等,则完全卸载前一个子树。
这不仅仅是性能问题 - 重新挂载组件会导致该组件及其所有子组件的状态丢失。
如果在组件之外创建 HOC,这样一来组件只会创建一次。因此,每次 render 时都会是同一个组件。一般来说,这跟你的预期表现是一致的。
在极少数情况下,你需要动态调用 HOC。你可以在组件的生命周期方法或其构造函数中进行调用。
4.Refs 不会被传递
虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。