React 精要面试题讲解(五) 高阶组件真解 (2)

顺便说下,一般像connect这样多嵌套了一层的高阶函数,我称之为二阶高阶函数。此后类推,三个括号就叫三阶高阶函数…

4. 高阶组件的两种形式(类比插槽)

同children 一样,高阶组件也存在组合(也可称之为代理)和继承两种形式。
那么高阶组件和children插槽有什么关系呢?
我们来类比以下代码:

//写一个组合形式的具备插槽功能的组件Provider class Provider extends React.Component{ render(){ return ( <div> {this.props.children} </div> ) } } // 下面是使用的方式 import ComA from './ComA' const useB = (<Provider > <ComA a='使用Provider时添加了a属性'/> </Provider >) // 写一个组合形式的高阶组件 const hocAddA = WrapedComponent => class NewComponent extends React.component{ render(){ return ( <div> <WrapedComponent a ='定义hocAddA时添加了a属性'/> </div> ) } } // 我们来尝试使用它 const NewA = hocAddA(ComA); // 或者 @hocAddA class ComB ...

ok, 上述两种代码在使用后的表现几乎是一致的(同样实现了给ComA添加属性a的功能)。
但是注意,插槽(children)的实现,不关心插槽组件的功能变化。只是把插槽当作当前组件的子组件去使用(这就是组合)。

//同样的,我现在这样使用 import ComA from './ComA' const useB = (<Provider > <ComA b='使用Provider时添加了b属性'/> </Provider >)

而高阶函数,在定义时就写死了参数组件的功能变化。
传入组件,得出的组件只会添加属性a。
当然,我们也可以通过二阶高阶函数实现 用参数控制参数组件的功能变化:

定义一个二阶高阶组件 const hocAddProps = props => WrapedComponent => class NewComponent extends React.Component{ render(){ return ( <div> <WrapedComponent {...props}/> </div> ) } } // 于是我们这样使用它 const propsAdded = { a: '添加了一个属性a', b: ‘添加了一个属性b' } const NewA = hocAddProps(propsAdded)(ComA)

诸如此类。实际上组合形式的高阶组件能做到的事,用children基本都能做到。

那么组合形式的高阶组件和继承形式的高阶组件的区别在哪呢?
组合形式(也称之为代理形式): 返回的新组件,继承的还是React.Component,只是把参数组件作为新组件的子组件去使用,能够实现给参数组件进行包装、属性的增删改、状态抽离等功能.
继承形式: 返回的新组件,继承的是 参数组件 ,从而实现以参数组件为模版,改写参数组件的功能。
上述划重点,要考。
我们再回过头来思考类的修饰器——返回一个新的类或改写参数类。
是不是一样的道理啊。
所以说高阶组件啥的,还是js啊,最多加了jsx的语法嘛。

5. 高阶组件之组合(代理)类型的高阶组件 上述我们已经知道了组合(代理)类型的高阶组件的概念和思想,以及它能实现的功能。

那么我们上demo代码

import React,{Component,createRef} from 'react'; export default title=>WrapedComponent=> class NewComponent extends Component{ //抽离状态 state={ value:'' } // 访问refs myref=createRef(); handleInputChange=(e)=>{ this.setState({ value:e.target.value }) } render(){ const {wap,...otherprops} = this.props; const newProps = { value:this.state.value, onChange:this.handleInputChange } //包装组件 return ( <div> 我是组件NewComponent,是典型的代理形式的高阶组件,我除了做自己的事,还可以对 我的参数组件: 1增加/删减props 2抽离状态 3访问ref 4包装组件 <div>我的title:{title}</div> <WrapedComponent {...otherprops} ref={this.myref} inputProps={newProps}/> </div> ) } }

这里要单独说一下上述功能中的状态抽离。
状态抽离(状态提升): 把参数组件(即代理形式中使用的子组件)的状态提升到NewComponent(即代理形式中的当前组件,也就是父组件) 中,这样一来,子组件只负责UI渲染,而父组件通过props传递state实现数据的控制
也就是说, NewComponent 成为参数组件的容器组件,参数组建单纯作为UI组件
ps: 容器组件和UI组件的概念是相对的。 例如 把B的状态抽离到父组件A上,那么A相对于B来说是B的容器组件,要这么去理解。后续讲react-redux中会提到。
___

6. 高阶组件之继承类型的高阶组件

同样的,上述我们已经知道了 继承类型的高阶组件的概念和思想,那么我们也直接上demo代码

import React from 'react' //这个是给返回的新组件起名用的函数,有兴趣可以结合调试器玩玩。 function getDisplayName(WrapedComponent){ return WrapedComponent.displayName||WrapedComponent.name||'component' } export default color=>WrapedComponent=> class NewComponent extends WrapedComponent{ // static displayName = `E(${getDisplayName(Inconponent)})` ; aaa = '我改写了参数组件中的aaa属性' compoenentDidMount(){ console.log('我不仅可以改写属性和方法,我还能改写钩子') } render(){ const {wap,...otherprops} = this.props; const element = super.render(); console.log(element); const newStyle = { color:element.type==='div'?color:null } const newProps = { ...otherprops, style:newStyle } // 我甚至还改写了参数组件的UI return React.cloneElement(element,newProps,element.props.children) } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgpzj.html