React高阶组件总结
在多个不同的组件中需要用到相同的功能,这个解决方法,通常有Mixin和高阶组件。
Mixin方法例如:
//给所有组件添加一个name属性
var defaultMixin = {
getDefaultProps: function() {
return {
name: "Allen"
}
}
} var Component = React.createClass({
mixins: [defaultMixin],
render: function() {
return <h1>Hello, {this.props.name}</h1>
}
})
但是由于Mixin过多会使得组件难以维护,在React ES6中Mixin不再被支持。
高阶组件是一个接替Mixin实现抽象组件公共功能的好方法。高阶组件其实是一个函数,接收一个组件作为参数,
返回一个包装组件作为返回值,类似于高阶函数。高阶组件和装饰器就是一个模式,因此,高阶组件可以作为
装饰器来使用。高阶组件有如下好处:
1. 适用范围广,它不需要es6或者其它需要编译的特性,有函数的地方,就有HOC。
2. Debug友好,它能够被React组件树显示,所以可以很清楚地知道有多少层,每层做了什么。
高阶组件基本形式:const EnhancedComponent = higherOrderComponent(WrappedComponent);
详细如下:
function hoc(ComponentClass) {
return class HOC extends React.Component {
componentDidMount() {
console.log("hoc");
} render() {
return <ComponentClass />
}
}
}
//使用高阶组件
class ComponentClass extends React.Component {
render() {
return <div></div>
}
} export default hoc(MyComponent); //作为装饰器使用
@hoc
export default class ComponentClass extends React.Component {
//...
}
高阶组件有两种常见的用法:
1. 属性代理(Props Proxy): 高阶组件通过ComponentClass的props来进行相关操作
2. 继承反转(Inheritance Inversion)): 高阶组件继承自ComponentClass
1. 属性代理(Props Proxy)
属性代理有如下4点常见作用:
1. 操作props
2. 通过refs访问组件实例
3. 提取state
4. 用其他元素包裹WrappedComponent,实现布局等目的
(1). 操作props
可以对原组件的props进行增删改查,通常是查找和增加,删除和修改的话,需要考虑到不能破坏原组件。
下面是添加新的props:
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
render() {
const newProps = {
user: currentLoggedInUser
}
return <WrappedComponent {...this.props} {...newProps}/>
}
}
}
(2). 通过refs访问组件实例
可以通过ref回调函数的形式来访问传入组件的实例,进而调用组件相关方法或其他操作。
例如:
//WrappedComponent初始渲染时候会调用ref回调,传入组件实例,在proc方法中,就可以调用组件方法
function refsHOC(WrappedComponent) {
return class RefsHOC extends React.Component {
proc(wrappedComponentInstance) {
wrappedComponentInstance.method()
} render() {
const props = Object.assign({}, this.props, {ref: this.proc.bind(this)})
return <WrappedComponent {...props}/>
}
}
}
(3). 提取state
你可以通过传入 props 和回调函数把 state 提取出来,类似于 smart component 与 dumb component。更多关于 dumb and smart component。
提取 state 的例子:提取了 input 的 value 和 onChange 方法。这个简单的例子不是很常规,但足够说明问题。
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
constructor(props) {
super(props)
this.state = {
name: ''
} this.onNameChange = this.onNameChange.bind(this)
}
onNameChange(event) {
this.setState({
name: event.target.value
})
}
render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onNameChange
}
}
return <WrappedComponent {...this.props} {...newProps}/>
}
}
} //使用方式如下
@ppHOC
class Example extends React.Component {
render() {
//使用ppHOC装饰器之后,组件的props被添加了name属性,可以通过下面的方法,将value和onChange添加到input上面
//input会成为受控组件
return <input name="name" {...this.props.name}/>
}
}
(4). 包裹WrappedComponent
为了封装样式、布局等目的,可以将WrappedComponent用组件或元素包裹起来。
例如:
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
render() {
return (
<div style={{display: 'block'}}>
<WrappedComponent {...this.props}/>
</div>
)
}
}
}
2. 继承反转(Inheritance Inversion)
HOC继承了WrappedComponent,意味着可以访问到WrappedComponent的state,props,生命周期和render方法。如果在HOC中定义了与WrappedComponent同名方法,将会发生覆盖,就必须手动通过super进行调用。通过完全操作WrappedComponent的render方法返回的元素树,可以真正实现渲染劫持。这种思想具有较强的入侵性。
大致形式如下:
function ppHOC(WrappedComponent) {
return class ExampleEnhance extends WrappedComponent {
...
componentDidMount() {
super.componentDidMount();
}
componentWillUnmount() {
super.componentWillUnmount();
}
render() {
...
return super.render();
}
}
}
例如,实现一个显示loading的请求。组件中存在网络请求,完成请求前显示loading,完成后再显示具体内容。
可以用高阶组件实现如下:
function hoc(ComponentClass) {
return class HOC extends ComponentClass {
render() {
if (this.state.success) {
return super.render()
}
return <div>Loading...</div>
}
}
} @hoc
export default class ComponentClass extends React.Component {
state = {
success: false,
data: null
};
async componentDidMount() {
const result = await fetch(...请求);
this.setState({
success: true,
data: result.data
});
}
render() {
return <div>主要内容</div>
}
}
(1) 渲染劫持
继承反转这种模式,可以劫持被继承class的render内容,进行修改,过滤后,返回新的显示内容。
之所以被称为渲染劫持是因为 HOC 控制着 WrappedComponent 的渲染输出,可以用它做各种各样的事。
通过渲染劫持,你可以完成:
在由 render输出的任何 React 元素中读取、添加、编辑、删除 props
读取和修改由 render 输出的 React 元素树
有条件地渲染元素树
把样式包裹进元素树,就行Props Proxy那样包裹其他的元素
注:在 Props Proxy 中不能做到渲染劫持。
虽然通过 WrappedComponent.prototype.render 你可以访问到 render 方法,不过还需要模拟 WrappedComponent 的实例和它的 props,还可能亲自处理组件的生命周期,而不是交给 React。记住,React 在内部处理了组件实例,你处理实例的唯一方法是通过 this 或者 refs。
例如下面,过滤掉原组件中的ul元素:
function hoc(ComponentClass) {
return class HOC extends ComponentClass {
render() {
const elementTree = super.render();
elementTree.props.children = elementTree.props.children.filter((z) => {
return z.type !== "ul" && z;
}
const newTree = React.cloneElement(elementTree);
return newTree;
}
}
} @hoc
export default class ComponentClass extends React.Component {
render() {
const divStyle = {
width: '100px',
height: '100px',
backgroundColor: 'red'
}; return (
<div>
<p style={{color: 'brown'}}>啦啦啦</p>
<ul>
<li>1</li>
<li>2</li>
</ul>
<h1>哈哈哈</h1>
</div>
)
}
}
(2) 操作state
HOC可以读取,编辑和删除WrappedComponent实例的state,可以添加state。不过这个可能会破坏WrappedComponent的state,所以,要限制HOC读取或添加state,添加的state应该放在单独的命名空间里,而不是和WrappedComponent的state混在一起。
例如:通过访问WrappedComponent的props和state来做调试
export function IIHOCDEBUGGER(WrappedComponent) {
return class II extends WrappedComponent {
render() {
return (
<div>
<h2>HOC Debugger Component</h2>
<p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>
<p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>
{super.render()}
</div>
)
}
}
}
(3) 条件渲染
当 this.props.loggedIn 为 true 时,这个 HOC 会完全渲染 WrappedComponent 的渲染结果。(假设 HOC 接收到了 loggedIn 这个 prop)
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
if (this.props.loggedIn) {
return super.render()
} else {
return null
}
}
}
}
(4) 解决WrappedComponent名字丢失问题
用HOC包裹的组件会丢失原先的名字,影响开发和调试。可以通过在WrappedComponent的名字上加一些前缀来作为HOC的名字,以方便调试。
例如:
//或
class HOC extends ... {
static displayName = `HOC(${getDisplayName(WrappedComponent)})`
...
} //getDisplayName
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName ||
WrappedComponent.name ||
‘Component’
}
(5) 实际应用
1. mobx-react就是高阶组件是一个实际应用
@observer装饰器将组件包装为高阶组件,传入组件MyComponent后,mobx-react会对其生命周期进行各种处理,并通过调用forceUpdate来进行刷新实现最小粒度的渲染。mobx提倡一份数据引用,而redux中则提倡immutable思想,每次返回新对象。
2. 实现一个从localStorage返回记录的功能
//通过多重高阶组件确定key并设定组件
const withStorage = (key) => (WrappedComponent) => {
return class extends Component {
componentWillMount() {
let data = localStorage.getItem(key);
this.setState({data});
}
render() {
return <WrappedComponent data={this.state.data} {...this.props} />
}
}
} @withStorage('data')
class MyComponent2 extends Component {
render() {
return <div>{this.props.data}</div>
}
} @withStorage('name')
class MyComponent3 extends Component {
render() {
return <div>{this.props.data}</div>
}
}
3. 实现打点计时功能
(1). Props Proxy方式
//性能追踪:渲染时间打点
export default (Target) => (props)=>{
let func1 = Target.prototype['componentWillMount']
let func2 = Target.prototype['componentDidMount']//Demo并没有在prototype上定义该方法,func2为undefined,但是并不会有影响,这样做只是为了事先提取出可能定义的逻辑,保持原函数的纯净
let begin, end;
Target.prototype['componentWillMount'] = function (...argus){//do not use arrow funciton to bind 'this' object
func1.apply(this,argus);//执行原有的逻辑
begin = Date.now();
}
Target.prototype['componentDidMount'] = function (...argus){
func2.apply(this,argus);//执行原有的逻辑
end = Date.now();
console.log(Target.name+'组件渲染时间:'+(end-begin)+'毫秒')
}
return <Target {...props}/>//do not forget to pass props to the element of Target
}
(2) Inheritance Inversion方式
// 另一种HOC的实现方式 Inheritance Inversion
export default Target => class Enhancer extends Target {
constructor(p){
super(p);//es6 继承父类的this对象,并对其修改,所以this上的属性也被继承过来,可以访问,如state
this.end =0;
this.begin=0;
}
componentWillMount(){
super.componentWilMount && super.componentWilMount();// 如果父类没有定义该方法,直接调用会出错
this.begin = Date.now();
}
componentDidMount(){
super.componentDidMount && super.componentDidMount();
this.end=Date.now();
console.log(Target.name+'组件渲染时间'+(this.end-this.begin)+'ms')
}
render(){
let ele = super.render();//调用父类的render方法
return ele;//可以在这之前完成渲染劫持
}
}
参考:https://zhuanlan.zhihu.com/p/24776678?group_id=802649040843051008
https://blog.csdn.net/cqm1994617/article/details/54800360
https://blog.csdn.net/xiangzhihong8/article/details/73459720
https://segmentfault.com/a/1190000004598113
https://blog.csdn.net/sinat_17775997/article/details/79087977
https://blog.csdn.net/neoveee/article/details/69212146
React高阶组件总结的更多相关文章
- 聊聊React高阶组件(Higher-Order Components)
使用 react已经有不短的时间了,最近看到关于 react高阶组件的一篇文章,看了之后顿时眼前一亮,对于我这种还在新手村晃荡.一切朝着打怪升级看齐的小喽啰来说,像这种难度不是太高同时门槛也不是那么低 ...
- 当初要是看了这篇,React高阶组件早会了
当初要是看了这篇,React高阶组件早会了. 概况: 什么是高阶组件? 高阶部件是一种用于复用组件逻辑的高级技术,它并不是 React API的一部分,而是从React 演化而来的一种模式. 具体地说 ...
- react高阶组件的理解
[高阶组件和函数式编程] function hello() { console.log('hello jason'); } function WrapperHello(fn) { return fun ...
- 函数式编程与React高阶组件
相信不少看过一些框架或者是类库的人都有印象,一个函数叫什么creator或者是什么什么createToFuntion,总是接收一个函数,来返回另一个函数.这是一个高阶函数,它可以接收函数可以当参数,也 ...
- React高阶组件学习笔记
高阶函数的基本概念: 函数可以作为参数被传递,函数可以作为函数值输出. 高阶组件基本概念: 高阶组件就说接受一个组件作为参数,并返回一个新组件的函数. 为什么需要高阶组件 多个组件都需要某个相同的功能 ...
- 利用 React 高阶组件实现一个面包屑导航
什么是 React 高阶组件 React 高阶组件就是以高阶函数的方式包裹需要修饰的 React 组件,并返回处理完成后的 React 组件.React 高阶组件在 React 生态中使用的非常频繁, ...
- react高阶组件的一些运用
今天学习了react高阶组件,刚接触react学习起来还是比较困难,和大家分享一下今天学习的知识吧,另外缺少的地方欢迎补充哈哈 高阶组件(Higher Order Components,简称:HOC) ...
- React——高阶组件
1.在React中higher-order component (HOC)是一种重用组件逻辑的高级技术.HOC不是React API中的一部分.HOC是一个函数,该函数接收一个组件并且返回一个新组件. ...
- react 高阶组件的 理解和应用
高阶组件是什么东西 简单的理解是:一个包装了另一个基础组件的组件.(相对高阶组件来说,我习惯把被包装的组件称为基础组件) 注意:这里说的是包装,可以理解成包裹和组装: 具体的是高阶组件的两种形式吧: ...
- react高阶组件的使用
为了提高代码的复用在react中我们可以使用高阶组件 1.添加高阶组件 高阶组件主要代码模板HOC.js export default (WrappedComponent) => { retur ...
随机推荐
- 容器云技术:容器化微服务,Istio占C位出道
在精彩的软件容器世界中,当新项目涌现并解决你认为早已解决的问题时,这感觉就像地面在你的脚下不断地移动.在许多情况下,这些问题很久以前被解决,但现在的云原生架构正在推动着更大规模的应用程序部署,这就需要 ...
- beauifulsoup模块的介绍
01 爬虫基础知识介绍 相关库:1.requests,re 2.BeautifulSoup 3.hackhttp 使用requests发起get,post请求,获取状态码,内容: 使用re匹 ...
- selenium自动化测试资源整理
1. 所有版本chrome下载 是不是很难找到老版本的chrome?博主收集了几个下载chrome老版本的网站,其中哪个下载的是原版的就不得而知了. http://www.slimjet.com/ch ...
- 进度条加载与案例优化对比——python使用perf_count方法实现
本章我们将讨论python3 perf_counter()的用法及它的实际应用我从中选取两个python基于rquests库的爬虫实例代码源文件进行举例 Python3 perf_counter() ...
- 小球下落 (Dropping Balls,UVA 679)
题目描述: 题目思路: 1.直接用数组模拟二叉树下落过程 //超时 #include <iostream> #include <cstring> using namespace ...
- n! 阶乘
其实1.2.3.4.6.7…都是可以不用考虑的,因此选择以5为迭代步数即可. 首先,这些数字都可以不用进行%5(对5取余数)运算,因此每次循环时可以直接将函数的count变量直接加1.其次,考虑25. ...
- Django创建App报错
在django下创建APP项目时遇到的坑 python manage.py startapp app01 报错内容如下: 解决:找到报错中的文件夹151行删除items(),)中的逗号即可 在命令行下 ...
- Ubuntu—截屏与截取选定区域
截屏:PrScrn(打印键) 截取选定区域:shift + PrScrn(打印键) # 截取选定区域时,先按下组合键后,鼠标的形状就会变成十字架形状,这时候再截取想要截取的区域就可以了-
- SpringCloud IDEA 教学 (五) 断路器控制台(HystrixDashboard)
写在开头 断路器控制台是为了查看断路器运行情况而研发的.本章介绍了断路器控制台的搭建,代码基于之前Client的搭建.HystrixDashboard基于之前配置好的,使用了HystrixComman ...
- Coprime Sequence(前后缀GCD)
Description Do you know what is called ``Coprime Sequence''? That is a sequence consists of $n$ posi ...