高阶组件类似于高阶函数,它接受React组件作为输入,输出一个新的React组件。当React组件被包裹时,高阶组件会返回一个增强的React组件,高阶组件可以提升代码的复用性、逻辑性、抽象性。
实现高阶组件有两种方式。
- 属性代理
- 反向继承 本篇文章主要分析属性代理的几种实现方式。
1. 属性代理
通过高阶组件来传递props,这种方法就是属性代理。下面对属性代理的几个功能进行讲解。
- 控制props
我们可以读取、增加、编辑或是移除从
WrappedComponent
传进来的props
但需要小心删除与编辑重要的props,应该尽可能对高阶组件的props做新的命名以防止混淆。
例如增加一个新的prop:
import React, { Component } from 'react';
const Mycontainer = (WrappedComponent) =>
class extends Component {
render () {
const newProps = {
text: 'newText'
}
return <WrappedComponent {...this.props} {...newProps}/>
}
}
export default Mycontainer
复制代码
当调用高阶组件事,可以用
text
这个新的props
了。对于原组件来说,只要套用这个高阶组件,新组件就会多一个属性。
import React, { Component } from 'react'
import Mycontainer from 'container/Mycontainer'
class Page1 extends Component {
render() {
console.log("this.props)
return <div>测试新添加一个属性</div>
}
}
export default Mycontainer(Page1)
复制代码
- 通过refs使用引用
在高阶组件中,我们可以接受
refs
使用WrappedComponent
的引用。例如:
import React, { Component } from 'react';
const Mycontainer = (WrappedComponent) =>
class extends Component {
proc(wrappedComponentInstance) {
console.log("ref回调函数被执行", wrappedComponentInstance)
console.log(wrappedComponentInstance.props);
wrappedComponentInstance.newText = "通过refs添加属性"
}
render () {
const props = Object.assign({}, this.props, {
ref: this.proc.bind(this),
});
return <WrappedComponent {...props} />
}
}
export default Mycontainer
复制代码
- 抽象state
import React, { Component } from 'react';
const Mycontainer = (WrappedComponent) =>
class extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
}
}
onNameChange = (event) => {
this.setState({
name: event.target.value,
})
}
render () {
const newProps = {
name: {
value: this.state.name,
onChange: this.onNameChange,
}
}
return <WrappedComponent {...this.props} {...newProps}/>
}
}
export default Mycontainer
复制代码
- 使用其他元素包裹
WrappedComponent
,这既可以为了增加样式,也可以是为了布局。
import React, { Component } from 'react';
const Mycontainer = (WrappedComponent) =>
class extends Component {
render () {
const newProps = {
text: 'newText'
}
return (
<div style={{display:"block"}}>
<WrappedComponent {...this.prop} />
</div>
)
}
}
export default Mycontainer
复制代码
2. 反向继承
另一种高阶组件的实现方式成为反向继承,一个简单的实现
const MyContainer = (WrappedComponent) => {
class extends WrappedComponent {
render() {
return super.render();
}
}
}
复制代码
正如所见,高阶组件返回的组件继承与WrappedComponent。因为被动的继承了WrappedComponent,所有的调用都会反向。 这种方法与属性代理不太一样。他通过继承WrappedComponent来实现,方法可以通过super来顺序调用,反向继承不能保证完整的子树被解析。 反向继承有两个比较大的特点。
- 渲染劫持
渲染劫持指的是高阶组件可以控制WrappedComponent的渲染过程,并渲染各种各样的结果。我们可以在这个过程中在任何React元素输出的结果中读取、增加、修改、删除props,或读取或修改React树,或条件显示树,又或是用样式控制包裹树。
- 一个条件渲染的示例:
const MyContainer = (WrappedComponent) => {
class extends WrappedComponent {
render() {
if(this.props.loggedIn) {
return super.render();
} else {
return null;
}
}
}
}
复制代码
- 我们可以对render的输出结果进行修改
const MyContainer = (WrappedComponent) => {
class extends WrappedComponent {
render() {
const elementsTree = super.render();
let newProps = {};
if(elementsTree && elementsTree.type === 'input') {
newProps = {
value:'may the force be with you'
};
}
const props = Object.assign({},elementsTree.props,newProps);
const newElementsTree = React.cloneElement(elementsTree,props,elementsTree.props.children);
return newElementsTree;
}
}
}
复制代码
- 控制state
高阶组件可以读取、修改或删除WrappedComponent实例中的state,如果需要的话,也可以增加state.但这样做,可能会让WrappedComponent组件内部变得一团糟。大部分高阶组件都应该限制读取或增加state,尤其是后者,可以通过重新命名state,以防止混淆。
const MyContainer = (WrappedComponent) => {
class extends WrappedComponent {
render() {
<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>
</div>
}
}
}
复制代码
这个例子中,显示了WrappedComponent的props和state,以方便我们在程序中区调试他们。
3.组件命名
4.组件参数
有时,我们调用高阶组件时需要传入一些参数。
function HOCFactoryFactory(...params) {
return function HOCFactory(WrappedComponent) {
return class HOC extends Component {
render() {
return <WrappedComponent {...this.props}
}
}
}
}
复制代码