React 之 高阶组件的理解
1.基本概念
高阶组件是参数为组件,返回值为新组件的函数。
2.举例说明
① 装饰工厂模式
组件是 react 中的基本单元,组件中通常有一些逻辑(非渲染)需要复用处理。这里我们可以用高阶组件对组件内部中的一些通用进行封装。
未封装时,相同的逻辑无法复用:
渲染评论列表
class CommentList extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
// 假设 "DataSource" 是个全局范围内的数据源变量
comments: DataSource.getComments()
};
}
componentDidMount() {
// 订阅更改
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
// 清除订阅
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
// 当数据源更新时,更新组件状态
this.setState({
comments: DataSource.getComments()
});
}
render() {
return (
<div>
{this.state.comments.map((comment) => (
<Comment comment={comment} key={comment.id} />
))}
</div>
);
}
}
渲染博客列表
lass 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} />;
}
}
借用高阶组件,封装公用逻辑:
const CommentListWithSubscription = withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
); const BlogPostWithSubscription = withSubscription(
BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id)
);
组件加工(加工:处理公用逻辑)工厂,接受旧组件,返回新组件:
// 此函数接收一个组件...
function withSubscription(WrappedComponent, selectData) {
// ...并返回另一个组件...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
} componentDidMount() {
// ...负责订阅相关的操作...
DataSource.addChangeListener(this.handleChange);
} componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
} handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
} render() {
// ... 并使用新数据渲染被包装的组件!
// 请注意,我们可能还会传递其他属性
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
② 高阶组件柯里化
高阶组件是一个参数是组件返回值也是组件的函数,那么我们借助函数柯里化,确保最终返回的函数是高阶组件就可以了。
import React, { Component } from "react";
const isEmpty = prop =>
(prop && prop.hasOwnProperty("length") && prop.length === 0) ||
(prop.constructor === Object && Object.keys(prop).length === 0);
export default loadingProp => WrappedComponent => {
const hocComponent = class extends Component {
componentDidMount() {
this.startTimer = Date.now();
}
componentWillUpdate(nextProps, nextState) {
console.log(nextProps)
if (!isEmpty(nextProps[loadingProp])) {
this.endTimer = Date.now();
}
}
render() {
const myProps = {
loadingTime: ((this.endTimer - this.startTimer) / 1000).toFixed(2)
};
return isEmpty(this.props[loadingProp]) ? (
<div>loading...</div>
) : (
<WrappedComponent {...this.props} {...myProps} />
);
}
};
return hocComponent;
};
其中 ,而 loadingProp => ... 是一个返回值为高阶组件的函数,其返回结果 WrappedComponent => {...} 是一个单参数(即被包裹的组件)的高阶组件。这样做有什么好处呢?为什么不写一个高阶组件并将参数与包装组件一并传递过去?高阶组件受限于返回值必须是组件,因此它无法柯里化。而在高阶组件之上再构建一个函数就能进行柯里化。同时,返回的结果是单参数(被包裹组件)的高阶组件可以直接做 hoc 嵌套,即 一个 hoc 嵌套 另一个 hoc(因为传入值、传出值都是 组件)。此外的对于 hoc 嵌套调用,可以借助 compose 工具函数 进行扁平化处理。
许多第三方库提供都提供了
compose工具函数,包括 lodash (比如lodash.flowRight), Redux 和 Ramda。
③ 设定 hoc 的显示名称 displayName
为了方便调试,设定 hoc 的显示名称类似: WithSubscription(CommentList)
function withSubscription(WrappedComponent) {
class WithSubscription extends React.Component {/* ... */}
WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
return WithSubscription;
}
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
3. 注意事项
① 不要在 render 方法中使用 hoc
因为 hoc 会产生新的组件,而 redner 方法经常被调用,所以会不断产生新的组件(而在 react 的 diff 算法中会将之前旧的卸载而替换成新的,这不仅仅会对性能造成影响,同时重新挂载组件会导致该组件及其所有子组件的状态丢失。
②务必复制静态方法
手动一个个复制
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
// 必须准确知道应该拷贝哪些方法 :(
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}
使用 hoist-non-react-statics 自动拷贝所有非 React 静态方法
import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
hoistNonReactStatic(Enhance, WrappedComponent);
return Enhance;
}
③ Ref 需要转发
虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。
使用 hoc 包裹组件
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
// 我们导出 LogProps,而不是 FancyButton。
// 虽然它也会渲染一个 FancyButton。
export default logProps(FancyButton);
形式上导入的是原组件,实际上导入的是 hoc 包裹的原组件。这时如果直接传 ref 到该组件,实际上 ref 并没有传递到原组件中,而停留在 hoc 组件上。
import FancyButton from './FancyButton'; const ref = React.createRef(); // 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。
// 尽管渲染结果将是一样的,
// 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
// 这意味着我们不能调用例如 ref.current.focus() 这样的方法
<FancyButton
label="Click Me"
handleClick={handleClick}
ref={ref}
/>;
这里我们需要对停留在 hoc 组件中的 ref 进行转发,使其传递到原组件中
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
233
React 之 高阶组件的理解的更多相关文章
- react 高阶组件的 理解和应用
高阶组件是什么东西 简单的理解是:一个包装了另一个基础组件的组件.(相对高阶组件来说,我习惯把被包装的组件称为基础组件) 注意:这里说的是包装,可以理解成包裹和组装: 具体的是高阶组件的两种形式吧: ...
- react高阶组件的理解
[高阶组件和函数式编程] function hello() { console.log('hello jason'); } function WrapperHello(fn) { return fun ...
- react.js 高阶组件----很简单的实例理解高阶组件思想
调试代码之前,我设置了两个缓存 分别是username和content 在控制台console设置两个缓存代码 localStorage.setItem('username','老王')localSt ...
- 【转】react的高阶组件
React进阶之高阶组件 前言 本文代码浅显易懂,思想深入实用.此属于react进阶用法,如果你还不了解react,建议从文档开始看起. 我们都知道高阶函数是什么, 高阶组件其实是差不多的用法,只 ...
- react之高阶组件(二)
高阶组件的使用 接上文———— 一.像函数一样直接调用 import React, { Component } from 'react' import A from './A' class C ext ...
- react用高阶组件实现路由守卫
react-router不像vue-router一样有很多钩子函数,可以做路由守卫.想实现路由守卫,可以用高阶组件来实现. @connect(state => ({ isLogin: state ...
- react之高阶组件(一)
当两个或多个组件有相同的地方,可以将相同的部分抽离出来 先创建三个组件A.B.C A.js import React, { Component } from 'react' class A exten ...
- React高阶组件总结
在多个不同的组件中需要用到相同的功能,这个解决方法,通常有Mixin和高阶组件. Mixin方法例如: //给所有组件添加一个name属性 var defaultMixin = { getDefaul ...
- React中的高阶组件,无状态组件,PureComponent
1. 高阶组件 React中的高阶组件是一个函数,不是一个组件. 函数的入参有一个React组件和一些参数,返回值是一个包装后的React组件.相当于将输入的React组件进行了一些增强.React的 ...
随机推荐
- HDU 4352:XHXJ's LIS
题目:(原题是英文而且很迷) 求区间内数的LIS长度==k的个数,比如153948的LIS为1 3 4 8,长度为4.据说这种题叫DP中DP,本来是线性,再套一层状压+数位,简直厉害到不行…… 线性的 ...
- Beginning Linux Programming 学习--chapter 11 Processes and Signals
What's process--什么是进程? The UNIX standards, specifically IEEE Std 1003.1, 2004 Edition, defines a pr ...
- Python中使用列表的一部分——参考Python编程从入门到实践
处理列表中的部分元素——切片 1. 切片 players = ['charles', 'martina', 'michael', 'florence', 'eli'] print(players[0: ...
- Python--列表中字符串按照某种规则排序的方法
作者:禅在心中 出处:http://www.cnblogs.com/pinking/ 本文版权归作者和博客园共有,欢迎批评指正及转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接, ...
- Python重要配置大全
PYTHON 环境安装 安装虚拟环境 pip install virtualenv 卸载包是用:pip uninstall virtualenv 快捷下载安装可用豆瓣源,方法为: pip instal ...
- 2019牛客暑期多校训练营(第四场)A meeting(dfs或dp,dp待更新)
示例1: 输入: 4 21 23 13 42 4 输出:2 说明: They can meet at place 1 or 3. 题意:从K个点到达不联通图某个点需要的最短时间,这个最短时间是这K个人 ...
- C++中深拷贝与浅拷贝
浅拷贝和深拷贝 在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B.这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指 ...
- Linux权限管理:setUID、setGID 和 Sticky BIT
1.setUID.setGID 和 Sticky BIT 的功能详解 setuid 功能: 1.只有可执行的二进制文件程序才能设定 SUID 权限(前提) 2.命令执行者要对该程序有执行(x)权限(必 ...
- 如何做好PPT
为什么要做ppt 全图型PPT,一张大图做背景,少量的文字---PPT大师Garr Renolds极力推崇的风格 半图型PPT PTT是为了和你的"客户"有效的沟通 好的PPT G ...
- java redisUtils工具类很全
GitHub地址:https://github.com/whvcse/RedisUtil redisUtils工具类: package com.citydo.utils; import org.spr ...