是什么
简称HOC,全称 High Order Component。作用是给react组件增减props属性。
怎么用
为什么不先说怎么写?恩,因为你其实已经用过了,举个例子:
// App.js
import {connect} from 'react-redux';
class App extends React.Component {
render() {}
}
export default connect()(App);
熟悉不?redux的连接器。不过笔者有洁癖,喜欢用装饰器:
// App.js
import {connect} from 'react-redux';
@connect()
export class App extends React.Component {
render() {}
}
开始写
从connect()()
可以看出,connect是一个函数,返回值是个react组件。这么聪明,好佩服自己啊。
雏形
// myHoc.js
import React from 'react';
export const myHoc = () => {
return (Wrapped) => {
class Hoc extends React.Component {
render() {
return <Wrapped {...this.props}>;
}
}
return Hoc;
};
};
是的,高阶组件的雏形,就是函数里隐藏了一个react组件,而参数Wrapped
是什么?就是下面被装饰的组件:
// App.js
@myHoc()
export class App extends React.Component {
render() {}
}
恩恩,表现形式和redux的connect一模一样。
所以用了高阶组件后,export出去的不再是你自己写的App(Class),而是最后一个高阶。
增加props属性
好的啦,现在用myHoc给App组件加点料:
// myHoc.js
export const myHoc = () => {
return (Wrapped) => {
class Hoc extends React.Component {
render() {
return <Wrapped {...this.props} whoAmI="原罪">;
}
}
return Hoc;
};
};
// App.js
@myHoc()
export class App extends React.Component {
render() {
return <div>{this.props.whoAmI}</div>;
}
}
放心,此刻浏览器里已经把我的名字 原罪 打印出来了。
多个高阶组件
是的,写完一个hoc之后,你就会有写第二个的需求,那就一起用呢:
// App.js
@myHoc()
@yourHoc()
@hisHoc()
@herHoc()
export class App extends React.Component {
render() {
return <div>{this.props.whoAmI}</div>;
}
}
这就是笔者为啥要用装饰器的原因,简洁,看起来舒服,写起来快,我们看一下另一种写法:
class App extends React.Component {
render() {}
}
export default myHoc()(yourHoc()(hisHoc()(herHoc()(App))));
自己体会,格式化一下吧:
class App extends React.Component {
render() {}
}
let hoc;
hoc = herHoc()(App);
hoc = hisHoc()(hoc);
hoc = yourHoc()(hoc);
hoc = myHoc()(hoc);
export default hoc;
写得累不?来,给你条毛巾擦擦汗
带参数
对了,hoc可以接收参数,比如这样:
// App.js
@myHoc('原罪2号')
export class App extends React.Component {
render() {
return <div>{this.props.whoAmI}</div>;
}
}
那高阶组件怎么接呢?
// myHoc.js
export const myHoc = (name) => {
return (Wrapped) => {
class Hoc extends React.Component {
render() {
return <Wrapped {...this.props} whoAmI={name}>;
}
}
return Hoc;
};
};
我把hoc接收到的参数又返还给了App组件,那现在浏览器输出的就是:原罪2号。
不带参数
现在,你可能有一个大胆的插法..哦不,想法,就是@myHoc后面可以不加括号吗?是哦,看前面几个案例,都是@myHoc()
。好的,看我的:
// myHoc.js
export const myHoc = (Wrapped) => {
class Hoc extends React.Component {
render() {
return <Wrapped {...this.props} whoAmI="原罪">;
}
}
return Hoc;
};
// App.js
@myHoc
export class App extends React.Component {
render() {
return <div>{this.props.whoAmI}</div>;
}
}
细心的看官看一下myHoc.js和带参数的时候有什么区别。是的,少了一层回调。如果你的高阶组件不需要带参数,这样写也是很ok的。
操控原组件
你可能需要拿被装饰的组件的state数据或者执行它的方法。那么需要建立一个引用:
// myHoc.js
import React from 'react';
export const myHoc = () => {
return (Wrapped) => {
class Hoc extends React.Component {
appRef = null;
componentDidMount() {
// 可以对被myHoc装饰的组件做羞羞的事情了,:)
console.log(this.appRef);
}
render() {
return <Wrapped {...this.props} ref={(app) => {this.appRef = app}} >;
}
}
return Hoc;
};
};
注意: 在多个高阶组件装饰同一个组件的情况下,此法并不奏效。你拿到的ref是上一个高阶组件的函数中临时生成的组件。而且在大多数情况下,你并不知道某个组件会被多少个高阶装饰!
总结
当项目中多处用到某个逻辑方法,但是这个逻辑不能放到util里的时候,HOC适合你。一个HOC最好只做一件事,这样维护方便