react学习之高阶组件

基本概念

高阶函数官网介绍:

1
2
高阶组件是参数为组件,返回值为新组件的函数。
组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。

所以高阶组件是一个函数而不是一个组件,它的作用是将组件转化为另一个组件。

使用高阶函数可以解决以下三个方面的问题:

  • 抽象出重复代码,实现组件复用
  • 控制组件的渲染逻辑,场景:权限控制
  • 劫持被处理组件的生命周期,场景:组件渲染性能追踪

实现

通常实现高阶组件的有两种方式:

  • 属性代理

    • 返回一个无状态的函数组件
    • 返回一个有状态的class组件
  • 反向继承

使用属性代理

操作props

使用HOC实现简单的属性代理,具体实现见 demo10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 返回一个无状态的函数组件
function HOC(WrappedComponent) {
const newProps = { type: 'HOC' };
return props => <WrappedComponent {...props} {...newProps}/>;
}

// 返回一个有状态的 class 组件
function HOC(WrappedComponent) {
return class extends React.Component {
render() {
const newProps = { type: 'HOC' };
return <WrappedComponent {...this.props} {...newProps}/>;
}
};
}

拦截父组件传递的props,添加type属性

抽象state

属性代理是无法直接操作原组件的state,但可以通过props和回调函数对state抽象。具体实现见

demo11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function HOC(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
};
this.onChange = this.onChange.bind(this);
}

onChange = (event) => {
this.setState({
name: event.target.value,
})
}

render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onChange,
},
};
return <WrappedComponent {...this.props} {...newProps} />;
}
};
}
// 使用
class Example extends React.Component {
render() {
return (
<div>
<input name="name" {...this.props.name} />
{this.props.name.value}
</div>
)
}
}

通过newProps来获取state中的值,并且调用onChange事件来实现更新state中的数据。

获取refs引用

使用ref属性可以访问到DOM(focus事件、动画、第三方DOM操作库)。ref属性只能声明在class类型的组件上,函数类型的组件无法声明,因为无状态组件没有实例。

同样,直接通过属性代理是无法获取原组件的refs引用,可以通过在原组件的ref回调函数中调用父组件传入的 ref 回调函数来获取原组件的refs 引用。官网也给出一个实现方法是通过使用React.forwardRef API。

获取static

当待处理的组件是class组件时,通过属性代理实现的高阶组件可以获取原组件的static方法。具体实现见具体实现见

demo12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function HOC(WrappedComponent) {
function wrappedComponentStaic () {
WrappedComponent.sayHello();
}

return (props) => (
<div>
<WrappedComponent
{...props}
/>
<input
type="button"
value="调用子组件static"
onClick={wrappedComponentStaic}
/>
</div>
);

}

// 使用
class Example extends React.Component {
static sayHello () {
console.error('hello world');
}
render() {
return (
<div>
<input name="name" />
</div>
)
}
}

props实现条件渲染

通过 props 来控制是否渲染及传入数据,利用三目运算判断

反向继承

反向继承是指使用一个函数接收一个组件作为参数传入,并返回一个继承了传入组件的类组件,且在返回组件的render()中返回super.render()

1
2
3
4
5
6
7
8
简单实现
const HOC = (WrappedComponent) => {
return class extends WrappedComponent {
render() {
return super.render();
}
}
}

它的特点是允许高阶组件通过this访问原组件,所以可以直接读取和操作原组件的state/ref/生命周期方法

注意事项

不要在render中使用HOC

重新挂载组件会导致组件及其所有子组件的状态丢失,如果在组件之外创建 HOC,这样一来组件只会创建一次

1
2
3
4
5
6
7
render() {
// 每次调用 render 函数都会创建一个新的 EnhancedComponent
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// 这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
return <EnhancedComponent />;
}

refs不会被传递

因为ref不是一个prop,它是由react专门处理的,如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。

查看评论