Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >你是如何使用React高阶组件的?_2023-02-28

你是如何使用React高阶组件的?_2023-02-28

原创
作者头像
用户10358021
发布于 2023-02-28 00:11:09
发布于 2023-02-28 00:11:09
60900
代码可运行
举报
文章被收录于专栏:前端面试2前端面试2
运行总次数:0
代码可运行

High Order Component(包装组件,后面简称HOC),是React开发中提高组件复用性的高级技巧。HOC并不是React的API,他是根据React的特性形成的一种开发模式。

HOC具体上就是一个接受组件作为参数并返回一个新的组件的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const EnhancedComponent = higherOrderComponent(WrappedComponent)

在React的第三方生态中,有非常多的使用,比如Redux的connect方法或者React-Router的withrouter方法。

举个例子

我们有两个组件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// CommentList
class CommentList extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      // "DataSource" is some global data source
      comments: DataSource.getComments()
    };
  }

  componentDidMount() {
    // Subscribe to changes
    DataSource.addChangeListener(this.handleChange);
  }

  componentWillUnmount() {
    // Clean up listener
    DataSource.removeChangeListener(this.handleChange);
  }

  handleChange() {
    // Update component state whenever the data source changes
    this.setState({
      comments: DataSource.getComments()
    });
  }

  render() {
    return (
      <div>
        {this.state.comments.map((comment) => (          <Comment comment={comment} key={comment.id} />
        ))}      </div>
    );
  }
}
代码语言:text
AI代码解释
复制
// BlogPost
class BlogPost extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      blogPost: DataSource.getBlogPost(props.id)
    };
  }

  componentDidMount() {
    DataSource.addChangeListener(this.handleChange);
  }

  componentWillUnmount() {
    DataSource.removeChangeListener(this.handleChange);
  }

  handleChange() {
    this.setState({
      blogPost: DataSource.getBlogPost(this.props.id)
    });
  }

  render() {
    return <TextBlock text={this.state.blogPost} />;
  }
}

他们虽然是两个不同的组件,对DataSource的需求也不同,但是他们有很多的内容是相似的:

  • 在组件渲染之后监听DataSource
  • 在监听器里面调用setState
  • 在unmout的时候删除监听器

在大型的工程开发里面,这种相似的代码会经常出现,那么如果有办法把这些相似代码提取并复用,对工程的可维护性和开发效率可以带来明显的提升。

使用HOC我们可以提供一个方法,并接受不了组件和一些组件间的区别配置作为参数,然后返回一个包装过的组件作为结果。

代码语言:text
AI代码解释
复制
function withSubscription(WrappedComponent, selectData) {
  // ...and returns another component...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }

    componentDidMount() {
      // ... that takes care of the subscription...
      DataSource.addChangeListener(this.handleChange);
    }

    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }

    handleChange() {
      this.setState({
        data: selectData(DataSource, this.props)
      });
    }

    render() {
      // ... and renders the wrapped component with the fresh data!
      // Notice that we pass through any additional props
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

然后我们就可以通过简单的调用该方法来包装组件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const CommentListWithSubscription = withSubscription(
  CommentList,
  (DataSource) => DataSource.getComments()
);

const BlogPostWithSubscription = withSubscription(
  BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id)
);

注意:在HOC中我们并没有修改输入的组件,也没有通过继承来扩展组件。HOC是通过组合的方式来达到扩展组件的目的,一个HOC应该是一个没有副作用的方法。

在这个例子中我们把两个组件相似的生命周期方法提取出来,并提供selectData作为参数让输入组件可以选择自己想要的数据。因为withSubscription是个纯粹的方法,所以以后如果有相似的组件,都可以通过该方法进行包装,能够节省非常多的重复代码。

不要修改原始组件,使用组合进行功能扩展

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function logProps(InputComponent) {
  InputComponent.prototype.componentWillReceiveProps = function(nextProps) {
    console.log('Current props: ', this.props);
    console.log('Next props: ', nextProps);
  };
  // The fact that we're returning the original input is a hint that it has
  // been mutated.
  return InputComponent;
}

// EnhancedComponent will log whenever props are received
const EnhancedComponent = logProps(InputComponent);

通过以上方式我们也可以达到扩展组件的效果,但是会存在一些问题

  • 如果InputComponent本身也有componentWillReceiveProps生命周期方法,那么就会被覆盖
  • functional component不适用,因为他根本不存在生命周期方法

修改原始组件的方式缺乏抽象化,使用者必须知道这个方法是如何实现的来避免上面提到的问题。

如果通过组合的方式来做,我们就可以避免这些问题

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function logProps(InputComponent) {
  return class extends React.Component{
    componentWillReceiveProps(nextProps) {
        console.log('Current props: ', this.props);
        console.log('Next props: ', nextProps);
    }
    render() {
        <InputComponent {...this.props} />
    }
  }
}

// EnhancedComponent will log whenever props are received
const EnhancedComponent = logProps(InputComponent);

惯例:无关的props传入到原始组件

HOC组件会在原始组件的基础上增加一些扩展功能使用的props,那么这些props就不应该传入到原始组件(当然有例外,比如HOC组件需要使用原始组件指定的props),一般来说我们会这样处理props:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
render() {
  // Filter out extra props that are specific to this HOC and shouldn't be
  // passed through
  const { extraProp, ...passThroughProps } = this.props;

  // Inject props into the wrapped component. These are usually state values or
  // instance methods.
  const injectedProp = someStateOrInstanceMethod;

  // Pass props to wrapped component
  return (
    <WrappedComponent
      injectedProp={injectedProp}
      {...passThroughProps}    />
  );
}

extraProp是HOC组件中要用的props,不用的剩下的props我们都认为是原始组件需要使用的props,如果是两者通用的props你可以单独传递。

惯例:包装组件的显示名称来方便调试

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function withSubscription(WrappedComponent) {
  class WithSubscription extends React.Component {/* ... */}
  WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
  return WithSubscription;
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

简单来说就是通过手动指定displayName来让HOC组件能够更方便得被react devtool观察到

惯例:不要在render方法里面调用HOC方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
render() {
  // A new version of EnhancedComponent is created on every render
  // EnhancedComponent1 !== EnhancedComponent2
  const EnhancedComponent = enhance(MyComponent);
  // That causes the entire subtree to unmount/remount each time!
  return <EnhancedComponent />;
}

一来每次调用enhance返回的都是一个新的class,react的diffing算法是根据组件的特征来判断是否需要重新渲染的,如果两次render的时候组件之间不是(===)完全相等的,那么会直接重新渲染,而部署根据props传入之后再进行diff,对性能损耗非常大。并且重新渲染会让之前的组件的state和children全部丢失。

二来React的组件是通过props来改变其显示的,完全没有必要每次渲染动态产生一个组件,理论上需要在渲染时自定义的参数,都可以通过事先指定好props来实现可配置。

静态方法必须被拷贝

有时候会在组件的class上面外挂一下帮助方法,如果按照上面的方法进行包装,那么包装之后的class就没有来这些静态方法,这时候为了保持组件使用的一致性,一般我们会把这些静态方法拷贝到包装后的组件上。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  // Must know exactly which method(s) to copy :(
  Enhance.staticMethod = WrappedComponent.staticMethod;
  return Enhance;
}

这个之适用于你已知输入组件存在那些静态方法的情况,如果需要可扩展性更高,那么可以选择使用第三方插件hoist-non-react-statics

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  hoistNonReactStatic(Enhance, WrappedComponent);
  return Enhance;
}

ref

ref作为React中的特殊属性--类似于key,并不属于props,也就是说我们使用传递props的方式并不会把ref传递进去,那么这时候如果我们在HOC组件上放一个ref,拿到的是包装之后的组件而不是原始组件,这可能就会导致一些问题。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
React的高阶组件怎么用?
高阶组件(HOC)是一个接收组件作为参数并返回新组件的函数。将多个组件的相同逻辑代码,抽象到HOC中,让组件更有结构化,更易于复用。HOC不破坏传入组件的特性,只通过组合形成新组件。HOC是纯函数,没有副作用。
Learn-anything.cn
2021/11/27
6000
React-代码复用(mixin.hoc.render props) 前言例子MixinHOCRender Props总结参考
最近在学习React的封装,虽然日常的开发中也有用到HOC或者Render Props,但从继承到组合,静态构建到动态渲染,都是似懂非懂,索性花时间系统性的整理,如有错误,请轻喷~~
菜的黑人牙膏
2019/04/09
1.6K0
React-代码复用(mixin.hoc.render props)
		前言例子MixinHOCRender Props总结参考
React学习(最终篇)—— 高阶应用:高阶组件(HOCs)
高阶组件(higher-order components:以下简称HOC或HOC组件)是一个React组件复用的高级技巧。HOCs本身并不是React的API接口,他是React组件之间组织方式的一种模式。
随风溜达的向日葵
2018/08/15
1.7K0
React面试八股文(第二期)
React.forwardRef 会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特别有用:
beifeng1996
2022/10/19
1.6K0
前端react面试题(边面边更)_2023-02-23
展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。
用户10358021
2023/02/23
7570
前端技能树,面试复习第 19 天—— React 基础一点通
React 并不是将 click 事件绑定到了 div 的真实 DOM 上,而是在 document 处监听了所有的事件,当事件发生并且冒泡到 document 处的时候,React 将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件。
前端修罗场
2023/10/07
3520
前端技能树,面试复习第 19 天—— React 基础一点通
React组件设计模式-纯组件,函数组件,高阶组件
如果你想写的组件只包含一个 render 方法,并且不包含 state,那么使用函数组件就会更简单。我们不需要定义一个继承于 React.Component 的类,我们可以定义一个函数,这个函数接收 props 作为参数,然后返回需要渲染的元素。
xiaofeng123aa
2022/10/18
2.2K0
2021高频前端面试题汇总之React篇
React并不是将click事件绑定到了div的真实DOM上,而是在document处监听了所有的事件,当事件发生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件。
zz1998
2021/09/15
2K0
React组件间逻辑复用
React 里,组件是代码复用的主要单元,基于组合的组件复用机制相当优雅。而对于更细粒度的逻辑(状态逻辑、行为逻辑等),复用起来却不那么容易:
ayqy贾杰
2019/06/12
1.5K0
React组件间逻辑复用
React系列-Mixin、HOC、Render Props
在讲react-hooks之前,我们来捋捋react状态逻辑复用相关知识点,这会帮助你理解hooks
落落落洛克
2021/01/08
2.4K0
React系列-Mixin、HOC、Render Props
React中的高阶组件
高阶组件HOC即Higher Order Component是React中用于复用组件逻辑的一种高级技巧,HOC自身不是React API的一部分,它是一种基于React的组合特性而形成的设计模式。
WindRunnerMax
2021/01/18
3.9K0
2022前端二面react面试题
(2)如果已经创建了 Create React App 项目,需要将 typescript 引入到已有项目中
hellocoder2028
2022/09/14
1.5K0
React进阶篇(一)高阶组件
高阶组件(Higher Order Component,HOC)是React的一种设计模式,用于增强现有组件的功能。
娜姐
2020/09/22
5640
美团前端二面经典react面试题总结_2023-03-01
React并不是将click事件绑定到了div的真实DOM上,而是在document处监听了所有的事件,当事件发生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件。
用户10376779
2023/03/01
1.5K0
前端常考react面试题(持续更新中)_2023-02-26
计算出Virtual DOM中真正变化的部分,并只针对该部分进行原生DOM操作,而非重新渲染整个页面。
用户10358021
2023/02/26
8860
字节前端必会react面试题1
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
beifeng1996
2023/01/03
3.3K0
前端常见react面试题合集_2023-03-15
简言之,HOC是一种组件的设计模式,HOC接受一个组件和额外的参数(如果需要),返回一个新的组件。HOC 是纯函数,没有副作用。
用户10376779
2023/03/15
2.5K0
react面试如何回答才能让面试官满意
注意:batchingStrategy 对象可以理解为“锁管理器”。这里的“锁”,是指 React 全局唯一的 isBatchingUpdates 变量,isBatchingUpdates 的初始值是 false,意味着“当前并未进行任何批量更新操作”。每当 React 调用 batchedUpdate 去执行更新动作时,会先把这个锁给“锁上”(置为 true),表明“现在正处于批量更新过程中”。当锁被“锁上”的时候,任何需要更新的组件都只能暂时进入 dirtyComponents 里排队等候下一次的批量更新,而不能随意“插队”。此处体现的“任务锁”的思想,是 React 面对大量状态仍然能够实现有序分批处理的基石。
beifeng1996
2022/09/22
9440
React 高阶组件HOC
概述 高阶组件( higher-order component ,HOC )是 React 中复用组件逻辑的一种进阶技巧,通俗的讲,高阶组件就是一个 React 组件包裹着另外一个 React 组件。它本身并不是 React 的 API,而是一种 React 组件设计理念,众多的 React 库已经证明了它的价值,例如耳熟能详的 react-redux。 高级组件使用函数来实现,基本上是一个类工厂,它的函数签名可以用类似 haskell 的伪代码表示: hocFactory:: W: React.Com
xiangzhihong
2018/02/06
1.7K0
前端一面高频react面试题(持续更新中)
React 中最常见的问题之一是组件不必要地重新渲染。React 提供了两个方法,在这些情况下非常有用:
beifeng1996
2022/10/27
1.9K0
相关推荐
React的高阶组件怎么用?
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验