react 高阶组件和 ES6 修饰器

在以前使用createClass创建组件的时候, react提供mixin方法将其他公用方法注入组件. 但是在extends Component时, 我们可以使用高阶组件以及@decorator来解决这个问题.

高阶组件的原理很简单: 提供一个函数, 函数接受一个组件, 返回一个新的组件. 由于js的特性, 组件也是一个函数, 也可以用来被继承, 所以出现了两种使用方式.

  1. 以子组件方式渲染原始组件(Props Proxy)
1
2
3
4
5
6
7
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
render() {
return <WrappedComponent {...this.props} />;
}
};
}

在这个高阶组件里, 你可以做一切想做的事情, 包括拦截props, 或者获取子组件实例, 用其他元素包裹原始组件等. 常用的connect方法就是一个这样的函数.

  1. 新组件继承子组件(Inheritance Inversion)
1
2
3
4
5
6
7
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
return super.render();
}
};
}

如同代码里看到的, 这种继承方式可以修改子组件的生命周期函数, 可以拦截子组件的render返回值, 并修改(这种情况对子组件是函数式组件无效). 一般用作拦截生命周期比较常见.

使用方法

  • 普通的用法, 类似let HighComponent = connect(...)(Component)
  • ES6提供的修饰器语法
1
2
@connect(params)
class MyComponent extends React.Component {...}

再来介绍下修饰器语法提供的一些便利.
比如, 想在开发阶段, 把所有有生命周期的组件函数, 在经历生命周期时候, 提供一个日志记录. 这种时候就是使用修饰器方法的时候了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function componentLogger(target) {
if (process.env.NODE_ENV !== 'development') {
return target;
}
for (const func in funcColors) {
if (target.prototype[func]) {
...//相关操作
}
}
return target;
}
export default componentLogger;
//使用
@componentLogger
class MyComponent extends React.Component {...}

修饰器如果有参数, 一般都可以做得通用一点, 判断如果传入值是function时, 直接使用默认值加载修饰器, 如果传入值是object时, 作为options生成一个新的修饰器, 修饰所需要的内容.

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
39
40
41
42
function ngCmd(arg) {
if (_.isFunction(arg)) {
const Wrapper = arg;
return class WrapperComponent extends React.Component {
render() {
return <NgCmd {...this.props} ><Wrapper {...this.props} /></NgCmd>;
}
}
} else if (_.isArray(arg)) {
let i = 0;
let args = [];
if (arg.length == 0) {
args = ['if', 'show', 'hide'];
} else {
['if', 'show', 'hide'].forEach((value, index) => {
if (arg.indexOf(value) > -1) {
i++;
}
})
if (i == 0) {
throw 'need at least one ng-cmd';
}
args = arg;
}
return (Wrapper) => {
return class WrapperComponent extends React.Component {
render() {
let cmdsObj = {};
['if', 'show', 'hide'].forEach((v, i) => {
if (args.indexOf(v) > -1 && _.has(this.props, v)) {
cmdsObj[v] = this.props[v];
}
})
return <NgCmd {...cmdsObj} ><Wrapper {...this.props} /></NgCmd>;
}
}
}
if (_.has(cmdsObj, 'if') && cmdsObj['if'] == false) {
return null;
}
}
}
作者

Mosby

发布于

2017-08-10

许可协议

评论