在以前使用createClass
创建组件的时候, react
提供mixin
方法将其他公用方法注入组件. 但是在extends Component
时, 我们可以使用高阶组件以及@decorator
来解决这个问题.
高阶组件的原理很简单: 提供一个函数, 函数接受一个组件, 返回一个新的组件. 由于js
的特性, 组件也是一个函数, 也可以用来被继承, 所以出现了两种使用方式.
- 以子组件方式渲染原始组件(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
方法就是一个这样的函数.
- 新组件继承子组件(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; } } }
|