什么是HOC?
HOC(全称Higher-order component)是一种React的进阶运用要领,重要照样为了便于组件的复用。HOC就是一个要领,猎取一个组件,返回一个更高等的组件。
什么时刻运用HOC?
在React开辟过程当中,发现有许多情况下,组件须要被”加强”,比方说给组件增添也许修正一些特定的props,一些权限的治理,也许一些其他的优化之类的。而假如这个功用是针对多个组件的,同时每个组件都写一套雷同的代码,显著显得不是很明智,所以就能够斟酌运用HOC。
栗子:react-redux的connect要领就是一个HOC,他猎取wrappedComponent,在connect中给wrappedComponent增添须要的props。
HOC的简朴完成
HOC不仅仅是一个要领,确实说应该是一个组件工场,猎取低阶组件,天生高阶组件。
一个最简朴的HOC完成是这个模样的:
function HOCFactory(WrappedComponent) {
return class HOC extends React.Component {
render(){
return <WrappedComponent {...this.props} />
}
}
}
HOC能够做什么?
- 代码复用,代码模块化
- 增编削props
- 衬着挟制
实在,除了代码复用和模块化,HOC做的实在就是挟制,由于传入的wrappedComponent是作为一个child举行衬着的,上级传入的props都是直接传给HOC的,所以HOC组件具有很大的权限去修正props和掌握衬着。
增编削props
能够经由过程对传入的props举行修正,也许增添新的props来到达增编削props的效果。
比方你想要给wrappedComponent增添一个props,能够这么搞:
function control(wrappedComponent) {
return class Control extends React.Component {
render(){
let props = {
...this.props,
message: "You are under control"
};
return <wrappedComponent {...props} />
}
}
}
如许,你就能够在你的组件中运用message这个props:
class MyComponent extends React.Component {
render(){
return <div>{this.props.message}</div>
}
}
export default control(MyComponent);
衬着挟制
这里的衬着挟制并非你能掌握它衬着的细节,而是掌握是不是去衬着。由于细节属于组件内部的render要领掌握,所以你没法掌握衬着细节。
比方,组件要在data没有加载完的时刻,实际loading…,就能够这么写:
function loading(wrappedComponent) {
return class Loading extends React.Component {
render(){
if(!this.props.data) {
return <div>loading...</div>
}
return <wrappedComponent {...props} />
}
}
}
这个模样,在父级没有传入data的时刻,这一块儿就只会显现loading…,不会显现组件的细致内容
class MyComponent extends React.Component {
render(){
return <div>{this.props.data}</div>
}
}
export default control(MyComponent);
HOC有什么用例?
React Redux
最典范的就是React Redux的connect要领(细致在connectAdvanced中完成)。
经由过程这个HOC要领,监听redux store,然后把下级组件须要的state(经由过程mapStateToProps猎取)和action creator(经由过程mapDispatchToProps猎取)绑定到wrappedComponent的props上。
logger和debugger
这个是官网上的一个示例,能够用来监控父级组件传入的props的转变:
function logProps(WrappedComponent) {
return class extends React.Component {
componentWillReceiveProps(nextProps) {
console.log(`WrappedComponent: ${WrappedComponent.displayName}, Current props: `, this.props);
console.log(`WrappedComponent: ${WrappedComponent.displayName}, Next props: `, nextProps);
}
render() {
// Wraps the input component in a container, without mutating it. Good!
return <WrappedComponent {...this.props} />;
}
}
}
页面权限治理
能够经由过程HOC对组件举行包裹,当跳转到当前页面的时刻,搜检用户是不是含有对应的权限。假如有的话,衬着页面。假如没有的话,跳转到其他页面(比方无权限页面,也许上岸页面)。
也能够给当前组件供应权限的API,页面内部也能够举行权限的逻辑推断。
底本预备把细致代码当个栗子贴出来的,效果倏忽想到公司保密协定,所以。。。
运用HOC须要注重什么?
只管不要随便修正下级组件须要的props
之所以这么说,是由于修正父级传给下级的props是有肯定风险的,可能会形成下级组件发作毛病。比方,底本须要一个name的props,然则在HOC中给删掉了,那末下级组件也许就没法一般衬着,以至报错。
Ref没法猎取你想要的ref
之前你在父组件中运用<component ref="component"/>
的时刻,你能够直接经由过程this.refs.component
举行猎取。然则由于这里的component经由HOC的封装,已是HOC内里的谁人component了,所以你没法猎取你想要的谁人ref(wrappedComponent的ref)。
要处理这个题目,这里有两个要领:
a) 像React Redux的connect要领一样,在内里增添一个参数,比方withRef
,组件中搜检到这个flag了,就给下级组件增添一个ref,并经由过程getWrappedInstance要领猎取。
栗子:
function HOCFactory(wrappedComponent) {
return class HOC extends React.Component {
getWrappedInstance = ()=>{
if(this.props.widthRef) {
return this.wrappedInstance;
}
}
setWrappedInstance = (ref)=>{
this.wrappedInstance = ref;
}
render(){
let props = {
...this.props
};
if(this.props.withRef) {
props.ref = this.setWrappedInstance;
}
return <wrappedComponent {...props} />
}
}
}
export default HOCFactory(MyComponent);
这模样你就能够在父组件中如许猎取MyComponent的ref值了。
class ParentCompoent extends React.Component {
doSomethingWithMyComponent(){
let instance = this.refs.child.getWrappedInstance();
// ....
}
render(){
return <MyComponent ref="child" withRef />
}
}
b) 另有一种要领,在官网中有提到过:
父级经由过程通报一个要领,来猎取ref,细致看栗子:
先看父级组件:
class ParentCompoent extends React.Component {
getInstance = (ref)=>{
this.wrappedInstance = ref;
}
render(){
return <MyComponent getInstance={this.getInstance} />
}
}
HOC内里把getInstance要领看成ref的要领传入就好
function HOCFactory(wrappedComponent) {
return class HOC extends React.Component {
render(){
let props = {
...this.props
};
if(typeof this.props.getInstance === "function") {
props.ref = this.props.getInstance;
}
return <wrappedComponent {...props} />
}
}
}
export default HOCFactory(MyComponent);
谢谢@wmzy的指出,在上面的两个要领
getInstance
和
setWrappedInstance
,由于
ES6 class
的写法并不会自动绑定
this
,所以
须要用bind(this)
到两个要领上,确保
this
的正确性。也许运用箭头函数来写两个要领,ES6的箭头函数会自动绑定
this
。
Component上面绑定的Static要领会丧失
比方,你本来在Component上面绑定了一些static要领MyComponent.staticMethod = o=>o
。然则由于经由HOC的包裹,父级组件拿到的已不是本来的组件了,所以固然没法猎取到staticMethod要领了。
官网上的示例:
// 定义一个static要领
WrappedComponent.staticMethod = function() {/*...*/}
// 应用HOC包裹
const EnhancedComponent = enhance(WrappedComponent);
// 返回的要领没法猎取到staticMethod
typeof EnhancedComponent.staticMethod === 'undefined' // true
这里有一个处理要领,就是hoist-non-react-statics
组件,这个组件会自动把一切绑定在对象上的非React要领都绑定到新的对象上:
import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
hoistNonReactStatic(Enhance, WrappedComponent);
return Enhance;
}
结束语
当你须要做React插件的时刻,HOC模子是一个很有用的模子。
愿望这篇文章能帮你对HOC有一个也许的相识和启示。
别的,这篇medium上的文章会给你更多的启示,在这篇文章中,我这里讲的被分为Props Proxy HOC,另有别的一种Inheritance Inversion HOC,强烈推荐看一看。