高阶组件的基本概念(是什么❓)
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
具体而言,高阶组件是参数为组件,不是函数,返回值为新组件的函数(在此之前可以先了解一下高阶函数)。
const EnhancedComponent = higherOrderComponent(WrappedComponent)
区别组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。概念比较简单,实际也好用。HOC 在 React 的第三方库中很常见,例如 Redux 的 connect 和 Relay 的 createFragmentContainer。
使用高阶组件的原因(为什么❓)
学习新的技术无非要么就是装杯💪💪,要么就是对于项目中带来了实际作用,并且能提升开发效率。(两者兼有)关于高阶组件能解决的问题可以简单概括成以下三个方面:
抽取重复代码,实现组件复用,常见场景:页面复用。条件渲染,控制组件的渲染逻辑(渲染劫持),常见场景:权限控制。捕获/劫持被处理组件的生命周期,常见场景:组件渲染性能追踪、日志打点
下面就来介绍实现方式,从而加深大家对高阶组件作用的理解。
高阶组件的实现(怎么做❓)
通常情况下,实现高阶组件的方式有以下两种:
属性代理(Props Proxy)
返回一个无状态(stateless)的函数组件返回一个 class 组件
反向继承(Inheritance Inversion)
高阶组件实现方式的差异性决定了它们各自的应用场景:一个 React 组件包含了 props、state、ref、生命周期方法、static方法和React 元素树几个重要部分,所以我将从以下几个方面对比两种高阶组件实现方式的差异性:
原组件是否被继承能否读取/操作原组件的 props能否读取/操作原组件的 state能否通过 ref 访问到原组件的 dom 元素是否影响原组件某些生命周期等方法是否取到原组件 static 方法能否劫持原组件生命周期方法能否渲染劫持
该方式实现的高阶组件会影响到元组件
操作props
import React from 'react';
function MyHoc(WrapperComponent) {
return class LogoProps extends React.Component {
render() {
const newProps = { type: 'logoProps'}
return (
<WrapperComponent {...this.props} {...newProps} />
export default MyHoc;
从上面代码可看出我们除了可以拦截到父组件的props,还可以对该props进行操作,例如增加一个type属性
抽象state
本质上来说高阶组件内是不能操作原组件state,但是非要这么干,就通过props回调的方式是可以实现的来看看吧:
import React from 'react';
function MyHoc(WrapperComponent) {
return class LogoProps extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
onChange = (e) => {
this.setState({
value: e.target.value
render() {
const newProps = {
name: {
value: this.state.value,
onChange: this.onChange
return (
<WrapperComponent {...this.props} {...newProps} />
export default MyHoc;
import React from 'react';
import MyHoc from './MyHoc';
class FancyComponent extends React.Component{
render() {
return (
<input {...this.props.name} />
export default MyHoc(FancyComponent);
获取静态方法
直接看栗子,比较简单:
MyHoc.js
import React from 'react';
function MyHoc(WrapperComponent) {
return class LogoProps extends React.Component {
componentDidMount() {
WrapperComponent.sayHello()
render() {
return (
<WrapperComponent {...this.props} />
export default MyHoc;
import React from 'react';
import MyHoc from './MyHoc';
import ReverseHoc from './ReverseHoc';
class FancyComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
static sayHello() {
console.log('say hello')
render() {
return (
<input {...this.props.name} />
export default MyHoc(FancyComponent);
输出:say hello
反向继承指的是使用一个函数接受一个组件作为参数传入,并返回一个继承了该传入组件的类组件,且在返回组件的 render() 方法中返回 super.render() 方法,最简单的实现如下:
const HOC = (WrappedComponent) => {
return class extends WrappedComponent {
render() {
return super.render();
与属性代理的区别是:使用反向继承方式实现的高阶组件的特点是允许高阶组件通过 this 访问到原组件,所以可以直接读取和操作原组件的 state/ref/生命周期方法。
劫持原组件生命周期
高阶组件会覆盖传入组件的生命周期,示例如下:
import React from 'react';
function ReverseHoc(WrapperComponent) {
return class LogoProps extends WrapperComponent {
componentDidMount() {
console.log('this is ReverseHoc componentDidMount Life');
render() {
super.render()
export default ReverseHoc;
import React from 'react';
import MyHoc from './MyHoc';
import ReverseHoc from './ReverseHoc';
class FancyComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
componentDidMount() {
console.log('this is FancyComponent componentDidMount Life')
render() {
return (
<input />
export default ReverseHoc(FancyComponent);
假如我不想覆盖,当传入组件有该生命周期,高阶组件就不覆盖,无则覆盖传入组件生命周期。
我们改造一下ReverseHoc.js:
import React from 'react';
function ReverseHoc(WrapperComponent) {
const DidMount = WrapperComponent.prototype.componentDidMount;
return class LogoProps extends WrapperComponent {
async componentDidMount() {
if(DidMount) {
await DidMount.apply(this)
console.log('this is ReverseHoc componentDidMount Life');
render() {
super.render()
export default ReverseHoc;
这样做就可以实现上面说的效果啦!
操作/读取state
因为我们高阶组件继承了传入组件,那么就是能访问到this了,有了this是不是就能操作和读取state,也就不用像属性代理那么复杂还要通过props回调来操作state示例:
import React from 'react';
function ReverseHoc(WrapperComponent) {
return class LogoProps extends WrapperComponent {
async componentDidMount() {
console.log(this.state)
this.setState({ type: 1 });
render() {
super.render()
export default ReverseHoc;
不要在 render 方法中使用 HOC务必复制静态方法Refs 不会被传递(下面有详细介绍)
Refs转发
基本概念:Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说,这通常不是必需的。但其对某些组件,尤其是可重用的组件库是很有用的。(应用于非受控组件)
过时API:String类型的Refs(官方不建议使用)
import React from 'react';
export default class UnControlledComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
submitVal: '',
handleSubmit = (e) =>
{
e.preventDefault()
this.setState({
submitVal: this.refs.inputRef.value
render() {
const { submitVal } = this.state;
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref="inputRef" />
</label>
<input type="submit" value="Submit" />
<h4>提交内容:{submitVal}</h4>
</form>
Refs回调
import React from 'react';
export default class UnControlledComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
submitVal: '',
this.inputRef = React.createRef();
handleSubmit = (e) => {
e.preventDefault()
this.setState({
submitVal: this.inputRef.value
render() {
const { submitVal } = this.state;
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={(ref) => this.inputRef = ref} />
</label>
<input type="submit" value="Submit" />
<h4>提交内容:{submitVal}</h4>
</form>
createRef方式
import React from 'react';
export default class UnControlledComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
inputVal: '',
submitVal: '',
this.inputRef = React.createRef();
handleSubmit = (e) => {
e.preventDefault()
this.setState({
submitVal: this.inputRef.current.value
render() {
const { submitVal } = this.state;
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.inputRef} />
</label>
<input type="submit" value="Submit" />
<h4>提交内容:{submitVal}</h4>
</form>
在高阶组件中转发 refs
首先我们看个示例:
import React from 'react';
const MyHoc = (WrapperComponent) => {
return class LogoProps extends React.Component {
render() {
const { forwardRef, ...rest } = this.props;
return (
<WrapperComponent {...rest} ref={forwardRef} />
export default MyHoc;
import MyHoc from './MyHoc';
import React from 'react';
class FancyInput extends React.Component {
render() {
return (
<input />
export default MyHoc(FancyInput)
import React from 'react';
import './App.css';
import FancyInput from './RefsOfHOC/FancyInput';
class App extends React.Component {
constructor(props) {
super(props);
this.forWardRef = React.createRef()
componentDidMount() {
console.log(this.forWardRef)
render() {
return (
<div className="App">
<FancyInput ref={this.forWardRef} />
</div>
export default App;
思考问题:
大家会觉得在App.js中componentDidMount输出的谁呢?
直接看结果,发现输出的高阶组件所包裹的LogoProps 实例
能不能会去到FancyInput.js Input ref呢?
肯定是可以的,上述示例肯定不是我们想要的,继续往下看
import React from 'react';
const MyHoc = (WrapperComponent) => {
class LogoProps extends React.Component {
render() {
const { forwardRef, ...rest } = this.props;
return (
<WrapperComponent {...rest} ref={forwardRef} />
return React.forwardRef((props, ref) => (
<LogoProps {...props} forwardRef={ref} />
export default MyHoc;
import MyHoc from './MyHoc';
import React from 'react';
const FancyInput = React.forwardRef((props, ref) => {
return (
<input ref={ref}/>
export default MyHoc(FancyInput)
import React from 'react';
import './App.css';
import FancyInput from './RefsOfHOC/FancyInput';
class App extends React.Component {
constructor(props) {
super(props);
this.forWardRef = React.createRef()
componentDidMount() {
console.log(this.forWardRef)
render() {
return (
<div className="App">
<FancyInput ref={this.forWardRef} />
</div>
export default App;
我们来看看结果:
拿到input Ref,就可以随意操作input组件了,例如:添加class,获取value值...等在上面的示例中,FancyInput 使用 React.forwardRef 来获取传递给它的 ref,然后转发到它渲染的 DOM input
以上内容如有遗漏错误,欢迎留言 ✍️指出,一起进步💪💪💪
如果觉得本文对你有帮助,🏀🏀留下你宝贵的 👍