深入理解React高阶组件

发布时间 - 2026-01-11 03:26:59    点击率:

1.在React中higher-order component (HOC)是一种重用组件逻辑的高级技术。HOC不是React API中的一部分。HOC是一个函数,该函数接收一个组件并且返回一个新组件。在React中,组件是代码复用的基本单位。

2.为了解释HOCs,举下面两个例子

CommentList组件会渲染出一个comments列表,列表中的数据来自于外部。

class CommentList extends React.Component {

  constructor() {

   super();

   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>

   );

  }

 } 

 接下来是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的不同方法,并且它们的输出也不一样,但是它们中的大部分实现是一样的:

1.装载完成后,给DataSource添加了一个change listener
2.当数据源发生变化后,在监听器内部调用setState
3.卸载之后,移除change listener

可以想象在大型应用中,相同模式的访问DataSource和调用setState会一次又一次的发生。我们希望抽象这个过程,从而让我们只在一个地方定义这个逻辑,然后在多个组件中共享。

接下来我们写一个创建组件的函数,这个函数接受两个参数,其中一个参数是组件,另一个参数是函数。下面调用withSubscription函数

const CommentListWithSubscription = withSubscription(

 CommentList,

 (DataSource) => DataSource.getComments()

);

 

const BlogPostWithSubscription = withSubscription(

 BlogPost,

 (DataSource, props) => DataSource.getBlogPost(props.id)

); 

调用withSubscription传的第一个参数是wrapped 组件,第二个参数是一个函数,该函数用于检索数据。

当CommentListWithSubscription和BlogPostWithSubscription被渲染,CommentList和BlogPost会接受一个叫做data的prop,data中保存了当前从DataSource中检索出的数据。withSubscription代码如下:

// This function takes a component...

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} />;

  }

 };

} 

 HOC并没有修改输入的组件,也没有使用继承去重用它的行为。HOC只是一个函数。wrapped 组件接受了容器的所以props,同时还接受了一个新的prop(data),data用于渲染wrapped 组件的输出。HOC不关心数据怎么使用也不关心数据为什么使用,wrapped组件不关心数据是哪儿得到。

因为withSubscription只是一个常规的函数,你能添加任意个数的参数。例如,你能让data prop的名字是可配置的,从而进一步将HOC与wrapped组件隔离。

或者接受一个配置shouldComponentUpdate,或者配置数据源的参数

使用高阶组件时有些需要注意的地方。

1.不要修改原始组件,这一点很重要

有如下例子:

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); 

这里存在一些问题,1.输入的组件不能与增强的组件单独重用。2.如果给EnhancedComponent应用其他的HOC,也会改变componentWillReceiveProps。

这个HOC对函数类型的组件不适用,因为函数类型组件没有生命周期函数HOC应该使用合成代替修改——通过将输入的组件包裹到容器组件中。

function logProps(WrappedComponent) {

 return class extends React.Component {

  componentWillReceiveProps(nextProps) {

   console.log('Current props: ', this.props);

   console.log('Next props: ', nextProps);

  }

  render() {

   // Wraps the input component in a container, without mutating it. Good!

   return <WrappedComponent {...this.props} />;

  }

 }

} 

这个新的logProps与旧的logProps有相同的功能,同时新的logProps避免了潜在的冲突。对class类型的组件和函数类型额组件同样适用。

2.不要在render方法中使用HOCs

React的diff算法使用组件的身份去决定是应该更新已存在的子树还是拆除旧的子树并装载一个新的,如果从render方法中返回的组件与之前渲染的组件恒等(===),那么React会通过diff算法更新之前渲染的组件,如果不相等,之前渲染的子树会完全卸载。 

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 />;

} 

 在组件定义的外部使用HOCs,以至于结果组件只被创建一次。在少数情况下,你需要动态的应用HOCs,你该在生命周期函数或者构造函数中做这件事

3.静态方法必须手动复制

有的时候在React组件上定义静态方法是非常有用的。当你给某个组件应用HOCs,虽然原始组件被包裹在容器组件里,但是返回的新组件不会有任何原始组件的静态方法。

// Define a static method

WrappedComponent.staticMethod = function() {/*...*/}

// Now apply an HOC

const EnhancedComponent = enhance(WrappedComponent);

 

// The enhanced component has no static method

typeof EnhancedComponent.staticMethod === 'undefined' // true 

 为了让返回的组件有原始组件的静态方法,就要在函数内部将原始组件的静态方法复制给新的组件。

function enhance(WrappedComponent) {

 class Enhance extends React.Component {/*...*/}

 // Must know exactly which method(s) to copy :(

  // 你也能够借助第三方工具

 Enhance.staticMethod = WrappedComponent.staticMethod;

 return Enhance;

} 

 4.容器组件上的ref不会传递给wrapped component

虽然容器组件上的props可以很简单的传递给wrapped component,但是容器组件上的ref不会传递到wrapped component。如果你给通过HOCs返回的组件设置了ref,这个ref引用的是最外层容器组件,而非wrapped 组件

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# React高阶组件  # react  # 高阶组件使用  # React 高阶组件HOC用法归纳  # React高阶组件的使用浅析  # React组件重构之嵌套+继承及高阶组件详解  # React学习笔记之高阶组件应用  # 浅谈React高阶组件  # React 高阶组件入门介绍  # react高阶组件经典应用之权限控制详解  # React HOC高阶组件深入讲解  # 子树  # 是一个  # 也不  # 周期函数  # 你给  # 不关心  # 的是  # 是一种  # 也会  # 第一个  # 让我们  # 多个  # 你也  # 其他的  # 要在  # 你能  # 这两个  # 这件事  # 第二个  # 能让 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 韩国服务器如何优化跨境访问实现高效连接?  javascript基于原型链的继承及call和apply函数用法分析  Laravel怎么配置自定义表前缀_Laravel数据库迁移与Eloquent表名映射【步骤】  家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?  什么是JavaScript解构赋值_解构赋值有哪些实用技巧  在Oracle关闭情况下如何修改spfile的参数  如何在建站之星绑定自定义域名?  Laravel怎么实现微信登录_Laravel Socialite第三方登录集成  如何在不使用负向后查找的情况下匹配特定条件前的换行符  百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧  长沙企业网站制作哪家好,长沙水业集团官方网站?  laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程  Laravel DB事务怎么使用_Laravel数据库事务回滚操作  Laravel如何实现密码重置功能_Laravel密码找回与重置流程  如何快速生成高效建站系统源代码?  如何选择可靠的免备案建站服务器?  ,网页ppt怎么弄成自己的ppt?  如何在香港免费服务器上快速搭建网站?  javascript事件捕获机制【深入分析IE和DOM中的事件模型】  大同网页,大同瑞慈医院官网?  文字头像制作网站推荐软件,醒图能自动配文字吗?  Angular 表单中正确绑定输入值以确保提交与验证正常工作  作用域操作符会触发自动加载吗_php类自动加载机制与::调用【教程】  如何在建站主机中优化服务器配置?  Laravel怎么使用artisan命令缓存配置和视图  Windows家庭版如何开启组策略(gpedit.msc)?(安装方法)  如何获取上海专业网站定制建站电话?  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  Laravel怎么调用外部API_Laravel Http Client客户端使用  如何在IIS中配置站点IP、端口及主机头?  如何在Windows虚拟主机上快速搭建网站?  Laravel怎么集成Log日志记录_Laravel单文件与每日日志配置及自定义通道【详解】  java ZXing生成二维码及条码实例分享  在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?  node.js报错:Cannot find module &#39;ejs&#39;的解决办法  如何用美橙互联一键搭建多站合一网站?  Laravel Sail是什么_基于Docker的Laravel本地开发环境Sail入门  Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程  Laravel Eloquent:优雅地将关联模型字段扁平化到主模型中  如何在自有机房高效搭建专业网站?  零基础网站服务器架设实战:轻量应用与域名解析配置指南  实现点击下箭头变上箭头来回切换的两种方法【推荐】  图册素材网站设计制作软件,图册的导出方式有几种?  Laravel与Inertia.js怎么结合_使用Laravel和Inertia构建现代单页应用  Mybatis 中的insertOrUpdate操作  Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理  Android okhttputils现在进度显示实例代码  Win11怎么关闭透明效果_Windows11辅助功能视觉效果设置  合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?  如何在Tomcat中配置并部署网站项目?