在网上针对HOC的深度好文很多,此文仅写给自己,一是记一记笔记,加深印象,二来也想谈谈自己的想法。
简而言之,HOC是对基础组件的再次包装,它可以劫持props、更改render,也可以做插件。和装饰器配合将现有的组件在不破坏结构的情况下添加内容,它也是一种设计模式,就像JS的工厂模式一样。将逻辑抽出来灵活复用。
如何使用呢?HOC有很多方式,这也是我学习了HOC迟迟不动手写博客的原因(过第一遍的时候一脸懵逼)先说最基础的方式吧。首先,写个简单的基础组件:
import React from 'react';
const Test2 = class extends React.Component {
render() {
return (
<div>
<input value={'hello world'} />
</div>
)
}
}
export default Test2
然后,另起炉灶写HOC,在这里,为了区别,我在外层先加了hello:
export default function wapper(Wap) {
return class extends React.Component {
render() {
return (
<>
<div>hello</div>
<Wap />
</>
)
}
}
}
最后,在父组件中引用他们:
import React, { Component } from 'react';
import Hoc from './test';
import Test2 from './test2';
const Home = Hoc(Test2)
export default Home
现在,一个非常简单的HOC就写出来了:
或者,这么写HOC也可:
export default (PackagedComponent, props) =>
class HOC extends React.Component {
render() {
return (
<>
<div>hello</div>
<PackagedComponent />
</>
)
}
}
更多写法可以见这个博客:
React高阶组件(HOC)的写法归纳](https://blog.youkuaiyun.com/huolinianyu/article/details/89353256)
能够实现简单的后,就开始实现它的专有功能:
1、更改props
其实这个功能在组件之间传值的时候可能也用过,这里再单独说一下在HOC中如何修改props:
方法1:
如果是在引用的时候多给参数的方法,类似以下代码:
const Home = Hoc(Test2, value)
那么HOC是这么更改的:
export default (PackagedComponent, props) =>
class HOC extends React.Component {
render() {
const newProps = {
...props,
d: 0
}
return (
<>
...
<PackagedComponent value={newProps} />
</>
)
}
}
2、控制渲染
这个原理其实和修改props很像,在render中我们可以通过props传值,通过专属字段控制是否渲染:
return (
<>
{this.props.bol?<div>hello</div>:''}
<PackagedComponent value={newProps} />
</>
)
3、渲染劫持
这部分十分的有趣,它的原理是HOC通过继承传入组件,修改传入组件的数值等达到渲染劫持的目的。
为了表述清除,我将传入组件、HOC、调用他们的组件(分开写的)全部贴出来。
传入组件:
const Test2 = class extends React.Component {
render() {
return (
<input value={'hello world'} />
)
}
}
HOC:
export default (PackagedComponent) =>
class HOC extends PackagedComponent {
render() {
const tree = super.render()
let newProps = {}
if (tree && tree.type === `input`) {
newProps = { value: "change" }
}
const newTree = React.cloneElement(tree, newProps)
return newTree
}
}
调用组件
const Home = Hoc(Test2)
export default Home
最终效果:
打印出的新树结构:
看起来渲染劫持可以有效更改某个节点/多个节点的值,但是,需要注意一个点,这个点和ref注意的点很像,就是如果传入组件中包含另一个完整的组件,那么包含的那个组件不会被渲染劫持,下面用代码解释。
我将传入的Test2换成这样的写法:
const Test2 = class extends React.Component {
render() {
return (
<div>
<div>
hello world
</div>
<Test3 />
</div>
)
}
}
const Test3 = class extends React.Component {
render() {
return (
<div>
我是另一个组件
</div>
)
}
}
HOC同样换个写法:
export default (PackagedComponent) =>
class HOC extends PackagedComponent {
render() {
console.log(super.render())
return super.render()
}
}
那么得到的结果如下图:
可以看到,完整的组件在劫持的时候不会返回详细的porps等信息。
渲染劫持的内容参考这篇文章
总结:HOC的学习大概写完了,文中没有提及的,是有大神用es7装饰器的写法写HOC,引用更加的方便。如果文中有问题,欢迎指正。