-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React 高阶组件(HOC)入门指南 #4
Comments
6666666 |
反向继承不能保证完整的子组件树被解析 感觉这个概念是个无用的概念,按照上文的描述: class WrappedComponent extends Component{
render(){
return(
<div>
<div>
<span>Hello World</span>
</div>
<MyFuncComponent />
<MyClassComponent />
</div>
)
}
}
const HOC = (WrappedComponent) =>
class extends WrappedComponent {
render() {
const elementsTree = super.render();
return elementsTree;
}
}
但是脱离 高阶组件 的概念,
这里变量 所以不知道是作者对于这句特性:反向继承不能保证完整的子组件树被解析 的理解错误了 还是 我的理解错误了 可以讨论下 搜了很多 这篇文章应该是很少对 反向继承不能保证完整的子组件树被解析 这句话有解释的文章 👍 |
一直有关注 HOC,最近因为用 II 的方式去改变一个组件库的 UI 显示,才稍微理解 HOC 的 II 方式
先说明,这里的解析是指对象解析,不是 DOM 解析。 可能搞混了直接调用 综上, |
@GeekaholicLin 谢谢指正
目前我对 React 渲染/更新的一点点浅显的了解(请指正、批评), 如果我使用了反向继承,在子类(子组件)中使用了 除非我删除相关的 我觉得很费解的是 解析 这个词的概念 希望能多分享一些修改组件库中相关的内容 可以让讨论更加有的放矢 其实我对这个 反向继承 的实际应用几乎没有看到过 感谢关于 |
@TomIsion 前边关于 关于实际应用,我稍微讲一下自己的使用,仅做参考吧,有错误还望指出。 处理过程大致如下:
https://gist.github.com/GeekaholicLin/98e02e566a823c4b351772e1c6406f04 II 的强大是由继承赋予的。 实际上,上边代码中 PS: 话说回来,官方推荐组合的形式,所以能不用继承就不用吧。说实话,在 React 里边写继承感觉挺绕的... |
@GeekaholicLin 感觉聊到最后其实还是没有办法很准备的给出
的准确真正的原因,按照你的解释,需要去详细了解下 我倒是很喜欢 React 可以提供的继承机制 (比 总之 感谢探讨 & 分享 |
@TomIsion 反向继承不能保证完整的子组件树被解析: |
写了点 demo |
之前的文章React Mixins入门指南介绍了React Mixin的使用。在实际使用中React Mixin的作用还是非常强大的,能够使得我们在多个组件中共用相同的方法。但是工程中大量使用Mixin也会带来非常多的问题。Dan Abramov在文章Mixins Considered Harmful
介绍了Mixin带来的一些问题,总结下来主要是以下几点:
为了处理上述的问题,React官方推荐使用高阶组件(High Order Component)
高阶组件(HOC)
刚开始学习高阶组件时,这个概念就透漏着高级的气味,看上去就像是一种先进的编程技术的一个深奥术语,毕竟名字里就有"高阶"这种字眼,实质上并不是如此。高阶组件的概念应该是来源于JavaScript的高阶函数:
这么看来柯里化也是高阶函数了。React官方定义高阶组件的概念是:
(本人也翻译了React官方文档的Advanced Guides部分,官方的高阶组件中文文档戳这里)
这么看来,高阶组件仅仅只是是一个接受组件组作输入并返回组件的函数。看上去并没有什么,那么高阶组件能为我们带来什么呢?首先看一下高阶组件是如何实现的,通常情况下,实现高阶组件的方式有以下两种:
属性代理
又是一个听起来很高大上的名词,实质上是通过包裹原来的组件来操作props,举个简单的例子:
上面的例子非常简单,但足以说明问题。我们可以看见函数HOC返回了新的组件(WrapperComponent),这个组件原封不动的返回作为参数的组件(也就是被包裹的组件:WrappedComponent),并将传给它的参数(props)全部传递给被包裹的组件(WrappedComponent)。这么看起来好像并没有什么作用,其实属性代理的作用还是非常强大的。
操作props
我们看到之前要传递给被包裹组件WrappedComponent的属性首先传递给了高阶组件返回的组件(WrapperComponent),这样我们就获得了props的控制权(这也就是为什么这种方法叫做属性代理)。我们可以按照需要对传入的props进行增加、删除、修改(当然修改带来的风险需要你自己来控制),举个例子:
在上面的例子中,我们为被包裹组件(WrappedComponent)新增加了固定的name属性,因此WrappedComponent组件中就会多一个name的属性。
获得
refs
的引用我们在属性代理中,可以轻松的拿到被包裹的组件的实例引用(
ref
),例如:上面的例子中,wrapperComponent渲染接受后,我们就可以拿到WrappedComponent组件的实例,进而实现调用实例方法的操作(当然这样会在一定程度上是反模式的,不是非常的推荐)。
抽象state
属性代理的情况下,我们可以将被包裹组件(WrappedComponent)中的状态提到包裹组件中,一个常见的例子就是实现不受控组件到受控的组件的转变(关于不受控组件和受控组件戳这里)
上面的例子中通过高阶组件,我们将不受控组件(WrappedComponent)成功的转变为受控组件.
用其他元素包裹组件
我们可以通过类似:
这种方式将被包裹组件包裹起来,来实现布局或者是样式的目的。
在属性代理这种方式实现的高阶组件,以上述为例,组件的渲染顺序是: 先WrappedComponent再WrapperComponent(执行ComponentDidMount的时间)。而卸载的顺序是先WrapperComponent再WrappedComponent(执行ComponentWillUnmount的时间)。
反向继承
反向继承是指返回的组件去继承之前的组件(这里都用WrappedComponent代指)
我们可以看见返回的组件确实都继承自WrappedComponent,那么所有的调用将是反向调用的(例如:
super.render()
),这也就是为什么叫做反向继承。渲染劫持
渲染劫持是指我们可以有意识地控制WrappedComponent的渲染过程,从而控制渲染控制的结果。例如我们可以根据部分参数去决定是否渲染组件:
甚至我们可以修改修改render的结果:
上面的例子中我们将WrappedComponent中的input元素value值修改为:
may the force be with you
。我们可以看到前后elementTree的区别:elementsTree:
newElementsTree:
在反向继承中,我们可以做非常多的操作,修改state、props甚至是翻转Element Tree。反向继承有一个重要的点: 反向继承不能保证完整的子组件树被解析,开始我对这个概念也不理解,后来在看了React Components, Elements, and Instances这篇文章之后对这个概念有了自己的一点体会。
React Components, Elements, and Instances这篇文章主要明确了一下几个点:
JSX
和React.createClass
创建的都是元素。所以, 反向继承不能保证完整的子组件树被解析的意思的解析的元素树中包含了组件(函数类型或者Class类型),就不能再操作组件的子组件了,这就是所谓的不能完全解析。举个例子:
我们可以查看解析的元素树(element tree),
div
下的span
是可以被完全被解析的,但是MyFuncComponent
和MyClassComponent
都是组件类型的,其子组件就不能被完全解析了。操作props和state
在上面的图中我们可以看到,解析的元素树(element tree)中含有
props
和state
(例子的组件中没有state),以及ref
和key
等值。因此,如果需要的话,我们不仅可以读取props
和state
,甚至可以修改增加、修改和删除。在某些情况下,我们可能需要为高阶属性传入一些参数,那我们就可以通过柯里化的形式传入参数,例如:
可以通过下面方式使用:
这种方式是不是非常类似于
React-Redux
库中的connect
函数,因为connect
也是类似的一种高阶函数。反向继承不同于属性代理的调用顺序,组件的渲染顺序是: 先WrappedComponent再WrapperComponent(执行ComponentDidMount的时间)。而卸载的顺序也是先WrappedComponent再WrapperComponent(执行ComponentWillUnmount的时间)。HOC和Mixin的比较
借用《深入React技术栈》一书中的图:
高阶组件属于函数式编程(functional programming)思想,对于被包裹的组件时不会感知到高阶组件的存在,而高阶组件返回的组件会在原来的组件之上具有功能增强的效果。而Mixin这种混入的模式,会给组件不断增加新的方法和属性,组件本身不仅可以感知,甚至需要做相关的处理(例如命名冲突、状态维护),一旦混入的模块变多时,整个组件就变的难以维护,也就是为什么如此多的React库都采用高阶组件的方式进行开发。
The text was updated successfully, but these errors were encountered: