高阶作用用于强化组件,复用逻辑,提升渲染性能等作用。
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
在react初期提供一种组合方法。通过React.createClass,加入mixins属性,具体用法和vue 中mixins相似。
const customMixin = {
componentDidMount(){
console.log( '------componentDidMount------' )
},
say(){
console.log(this.state.name)
}
}
const APP = React.createClass({
mixins: [ customMixin ],
getInitialState(){
return {
name:'alien'
}
},
render(){
const { name } = this.state
return <div> hello ,world , my name is { name } </div>
}
})
这种mixins只能存在createClass中,后来React.createClass连同mixins这种模式被废弃了。mixins会带来一些负面的影响。
我们可以通过原型链继承来实现mixins。
const customMixin = { /* 自定义 mixins */
componentDidMount(){
console.log( '------componentDidMount------' )
},
say(){
console.log(this.state.name)
}
}
function componentClassMixins(Component,mixin){ /* 继承 */
for(let key in mixin){
Component.prototype[key] = mixin[key]
}
}
class Index extends React.Component{
constructor(){
super()
this.state={ name:'alien' }
}
render(){
return <div> hello,world
<button onClick={ this.say.bind(this) } > to say </button>
</div>
}
}
componentClassMixins(Index,customMixin)
作者:我不是外星人
链接:https://juejin.cn/post/6940422320427106335
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。const customMixin = { /* 自定义 mixins */
componentDidMount(){
console.log( '------componentDidMount------' )
},
say(){
console.log(this.state.name)
}
}
function componentClassMixins(Component,mixin){ /* 继承 */
for(let key in mixin){
Component.prototype[key] = mixin[key]
}
}
class Index extends React.Component{
constructor(){
super()
this.state={ name:'alien' }
}
render(){
return <div> hello,world
<button onClick={ this.say.bind(this) } > to say </button>
</div>
}
}
componentClassMixins(Index,customMixin)
在class组件盛行之后,我们可以通过继承的方式进一步的强化我们的组件。这种模式的好处在于,可以封装基础功能组件,然后根据需要去extends我们的基础组件,按需强化组件,但是值得注意的是,必须要对基础组件有足够的掌握,否则会造成一些列意想不到的情况发生。
class Base extends React.Component{
constructor(){
super()
this.state={
name:'alien'
}
}
say(){
console.log('base components')
}
render(){
return <div> hello,world <button onClick={ this.say.bind(this) } >点击</button> </div>
}
}
class Index extends Base{
componentDidMount(){
console.log( this.state.name )
}
say(){ /* 会覆盖基类中的 say */
console.log('extends components')
}
}
export default Index
function HOC(Component) {
return class wrapComponent extends React.Component{
constructor(){
super()
this.state={
name:'alien'
}
}
render=()=><Component { ...this.props } { ...this.state } />
}
}
@HOC
class Index extends React.Component{
say(){
const { name } = this.props
console.log(name)
}
render(){
return <div> hello,world <button onClick={ this.say.bind(this) } >点击</button> </div>
}
}
hooks解决无状态组件没有state和逻辑难以复用问题。hooks可以将一段逻辑封装起来,做到开箱即用。
组件是把prop渲染成UI,而高阶组件是将组件转换成另外一个组件,我们更应该注意的是,经过包装后的组件,获得了那些强化,节省多少逻辑,或是解决了原有组件的那些缺陷,这就是高阶组件的意义。
@withStyles(styles)
@withRouter
@keepaliveLifeCycle
class Index extends React.Componen{
/* ... */
}
越靠近Index组件的,就是越内层的HOC,离组件Index也就越近。
function Index(){
/* .... */
}
export default withStyles(styles)(withRouter( keepaliveLifeCycle(Index) ))
对于不需要传递参数的HOC,我们编写模型我们只需要嵌套一层就可以,比如withRouter。
function withRouter(){
return class wrapComponent extends React.Component{
/* 编写逻辑 */
}
}
对于需要参数的HOC,我们需要一层代理。
function connect (mapStateToProps){
/* 接受第一个参数 */
return function connectAdvance(wrapCompoent){
/* 接受组件 */
return class WrapComponent extends React.Component{ }
}
}
对于代理函数,可能有一层,可能有很多层。
正向的属性代理和反向的组件继承
用组件包裹一层代理组件, 在代理组件上,我们可以做一些,对源组件的代理操作。在fiber tree 上,先mounted代理组件,然后才是我们的业务组件。我们可以理解为父子组件关系,父组件对子组件进行一系列强化操作。
function HOC(WrapComponent){
return class Advance extends React.Component{
state={
name:'alien'
}
render(){
return <WrapComponent { ...this.props } { ...this.state } />
}
}
}
优点:
缺点:
① 一般无法直接获取业务组件的状态,如果想要获取,需要ref获取组件实例。
② 无法直接继承静态属性。如果需要继承需要手动处理,或者引入第三方库。
class Index extends React.Component{
render(){
return <div> hello,world </div>
}
}
Index.say = function(){
console.log('my name is alien')
}
function HOC(Component) {
return class wrapComponent extends React.Component{
render(){
return <Component { ...this.props } { ...this.state } />
}
}
}
const newIndex = HOC(Index)
console.log(newIndex.say)
反向继承和属性代理有一定的区别,在于包装后的组件继承了业务组件本身,所以我们我无须在去实例化我们的业务组件。当前高阶组件就是继承后,加强型的业务组件。
class Index extends React.Component{
render(){
return <div> hello,world </div>
}
}
function HOC(Component){
return class wrapComponent extends Component{ /* 直接继承需要包装的组件 */
}
}
export default HOC(Index)
优点:
class Index extends React.Component{
render(){
return <div> hello,world </div>
}
}
Index.say = function(){
console.log('my name is alien')
}
function HOC(Component) {
return class wrapComponent extends Component{
}
}
const newIndex = HOC(Index)
console.log(newIndex.say)
缺点:
这个是高阶组件最常用的功能,承接上层的props,在混入自己的props,来强化组件。
有状态组件:
function classHOC(WrapComponent){
return class Idex extends React.Component{
state={
name:'alien'
}
componentDidMount(){
console.log('HOC')
}
render(){
return <WrapComponent { ...this.props } { ...this.state } />
}
}
}
function Index(props){
const { name } = props
useEffect(()=>{
console.log( 'index' )
},[])
return <div>
hello,world , my name is { name }
</div>
}
export default classHOC(Index)
无状态组件:
function functionHoc(WrapComponent){
return function Index(props){
const [ state , setState ] = useState({ name :'alien' })
return <WrapComponent { ...props } { ...state } />
}
}
高阶组件可以将HOC的state的配合起来,控制业务组件的更新。这种用法在react-redux中connect高阶组件中用到过,用于处理来自redux中state更改,带来的订阅更新作用。
function classHOC(WrapComponent){
return class Idex extends React.Component{
constructor(){
super()
this.state={
name:'alien'
}
}
changeName(name){
this.setState({ name })
}
render(){
return <WrapComponent { ...this.props } { ...this.state } changeName={this.changeName.bind(this) } />
}
}
}
function Index(props){
const [ value ,setValue ] = useState(null)
const { name ,changeName } = props
return <div>
<div> hello,world , my name is { name }</div>
改变name <input onChange={ (e)=> setValue(e.target.value) } />
<button onClick={ ()=> changeName(value) } >确定</button>
</div>
}
export default classHOC(Index)
对于属性代理的高阶组件,虽然不能在内部操控渲染状态,但是可以在外层控制当前组件是否渲染,这种情况应用于,权限隔离,懒加载 ,延时加载等场景。
实现一个动态挂载组件的HOC
function renderHOC(WrapComponent){
return class Index extends React.Component{
constructor(props){
super(props)
this.state={ visible:true }
}
setVisible(){
this.setState({ visible:!this.state.visible })
}
render(){
const { visible } = this.state
return <div className="box" >
<button onClick={ this.setVisible.bind(this) } > 挂载组件 </button>
{ visible ? <WrapComponent { ...this.props } setVisible={ this.setVisible.bind(this) } /> : <div className="icon" ><SyncOutlined spin className="theicon" /></div> }
</div>
}
}
}
class Index extends React.Component{
render(){
const { setVisible } = this.props
return <div className="box" >
<p>hello,my name is alien</p>
<img src='https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=294206908,2427609994&fm=26&gp=0.jpg' />
<button onClick={() => setVisible()} > 卸载当前组件 </button>
</div>
}
}
export default renderHOC(Index)
实现一个懒加载功能的HOC,可以实现组件的分片渲染,用于分片渲染页面,不至于一次渲染大量组件造成白屏效果。
const renderQueue = []
let isFirstrender = false
const tryRender = ()=>{
const render = renderQueue.shift()
if(!render) return
setTimeout(()=>{
render() /* 执行下一段渲染 */
},300)
}
/* HOC */
function renderHOC(WrapComponent){
return function Index(props){
const [ isRender , setRender ] = useState(false)
useEffect(()=>{
renderQueue.push(()=>{ /* 放入待渲染队列中 */
setRender(true)
})
if(!isFirstrender) {
tryRender() /**/
isFirstrender = true
}
},[])
return isRender ? <WrapComponent tryRender={tryRender} { ...props } /> : <div className='box' ><div className="icon" ><SyncOutlined spin /></div></div>
}
}
/* 业务组件 */
class Index extends React.Component{
componentDidMount(){
const { name , tryRender} = this.props
/* 上一部分渲染完毕,进行下一部分渲染 */
tryRender()
console.log( name+'渲染')
}
render(){
return <div>
<img src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=294206908,2427609994&fm=26&gp=0.jpg" />
</div>
}
}
/* 高阶组件包裹 */
const Item = renderHOC(Index)
export default () => {
return <React.Fragment>
<Item name="组件一" />
<Item name="组件二" />
<Item name="组件三" />
</React.Fragment>
}
初始化的时候,HOC中将渲染真正组件的渲染函数,放入renderQueue队列中,然后初始化渲染一次,接下来,每一个项目组件,完成 didMounted 状态后,会从队列中取出下一个渲染函数,渲染下一个组件, 一直到所有的渲染任务全部执行完毕,渲染队列清空,有效的进行分片的渲染。
用HOC实现了条件渲染-分片渲染的功能,实际条件渲染理解起来很容易,就是通过变量,控制是否挂载组件,从而满足项目本身需求,条件渲染可以演变成很多模式。
dva里面的dynamic就是应用HOC模式实现的组件异步加载。
/* 路由懒加载HOC */
export default function AsyncRouter(loadRouter) {
return class Content extends React.Component {
state = {Component: null}
componentDidMount() {
if (this.state.Component) return
loadRouter()
.then(module => module.default)
.then(Component => this.setState({Component},
))
}
render() {
const {Component} = this.state
return Component ? <Component {
...this.props
}
/> : null
}
}
}
const Index = AsyncRouter(()=>import('../pages/index'))
hoc还可以配合其他API,做一下衍生的功能。如上配合import实现异步加载功能。
HOC反向继承模式,可以实现颗粒化的渲染劫持,也就是可以控制基类组件的render函数,还可以篡改props,或者是children。
const HOC = (WrapComponent) =>
class Index extends WrapComponent {
render() {
if (this.props.visible) {
return super.render()
} else {
return <div>暂无数据</div>
}
}
}
修改渲染状态(劫持render替换子节点)
class Index extends React.Component{
render(){
return <div>
<ul>
<li>react</li>
<li>vue</li>
<li>Angular</li>
</ul>
</div>
}
}
function HOC (Component){
return class Advance extends Component {
render() {
const element = super.render()
const otherProps = {
name:'alien'
}
/* 替换 Angular 元素节点 */
const appendElement = React.createElement('li' ,{} , `hello ,world , my name is ${ otherProps.name }` )
const newchild = React.Children.map(element.props.children.props.children,(child,index)=>{
if(index === 2) return appendElement
return child
})
return React.cloneElement(element, element.props, newchild)
}
}
}
export default HOC(Index)
用劫持渲染的方式,来操纵super.render()后的React.element元素,然后配合 createElement , cloneElement , React.Children 等 api,可以灵活操纵,真正的渲染react.element。
hoc除了可以进行条件渲染,渲染劫持功能外,还可以进行节流渲染,也就是可以优化性能。
hoc可以配合hooks的useMemo等API配合使用,可以实现对业务组件的渲染控制,减少渲染次数,从而达到优化性能的效果。如下案例,我们期望当且仅当num改变的时候,渲染组件,但是不影响接收的props。
function HOC (Component){
return function renderWrapComponent(props){
const { num } = props
const RenderElement = useMemo(() => <Component {...props} /> ,[ num ])
return RenderElement
}
}
class Index extends React.Component{
render(){
console.log(`当前组件是否渲染`,this.props)
return <div>hello,world, my name is alien </div>
}
}
const IndexHoc = HOC(Index)
export default ()=> {
const [ num ,setNumber ] = useState(0)
const [ num1 ,setNumber1 ] = useState(0)
const [ num2 ,setNumber2 ] = useState(0)
return <div>
<IndexHoc num={ num } num1={num1} num2={ num2 } />
<button onClick={() => setNumber(num + 1) } >num++</button>
<button onClick={() => setNumber1(num1 + 1) } >num1++</button>
<button onClick={() => setNumber2(num2 + 1) } >num2++</button>
</div>
}
我们只有点击 num++时候,才重新渲染子组件,点击其他按钮,只是负责传递了props,达到了期望的效果。
我们需要对上述hoc进行改造升级,是组件可以根据定制化方向,去渲染组件。也就是Hoc生成的时候,已经按照某种契约去执行渲染。
function HOC (rule){
return function (Component){
return function renderWrapComponent(props){
const dep = rule(props)
const RenderElement = useMemo(() => <Component {...props} /> ,[ dep ])
return RenderElement
}
}
}
/* 只有 props 中 num 变化 ,渲染组件 */
@HOC( (props)=> props['num'])
class IndexHoc extends React.Component{
render(){
console.log(`组件一渲染`,this.props)
return <div> 组件一 : hello,world </div>
}
}
/* 只有 props 中 num1 变化 ,渲染组件 */
@HOC((props)=> props['num1'])
class IndexHoc1 extends React.Component{
render(){
console.log(`组件二渲染`,this.props)
return <div> 组件二 : my name is alien </div>
}
}
export default ()=> {
const [ num ,setNumber ] = useState(0)
const [ num1 ,setNumber1 ] = useState(0)
const [ num2 ,setNumber2 ] = useState(0)
return <div>
<IndexHoc num={ num } num1={num1} num2={ num2 } />
<IndexHoc1 num={ num } num1={num1} num2={ num2 } />
<button onClick={() => setNumber(num + 1) } >num++</button>
<button onClick={() => setNumber1(num1 + 1) } >num1++</button>
<button onClick={() => setNumber2(num2 + 1) } >num2++</button>
</div>
}
可以灵活控制React组件层面上的,props数据流和更新流。
外生命周期,劫持事件,监控日志等等。
劫持原型链, 劫持生命周期,事件函数。
function HOC (Component){
const proDidMount = Component.prototype.componentDidMount
Component.prototype.componentDidMount = function(){
console.log('劫持生命周期:componentDidMount')
proDidMount.call(this)
}
return class wrapComponent extends React.Component{
render(){
return <Component {...this.props} />
}
}
}
@HOC
class Index extends React.Component{
componentDidMount(){
console.log('———didMounted———')
}
render(){
return <div>hello,world</div>
}
}
因为在继承原有组件的基础上,可以对原有组件的生命周期或事件进行劫持,甚至是替换。
function HOC (Component){
const didMount = Component.prototype.componentDidMount
return class wrapComponent extends Component{
componentDidMount(){
console.log('------劫持生命周期------')
if (didMount) {
didMount.apply(this) /* 注意 `this` 指向问题。 */
}
}
render(){
return super.render()
}
}
}
@HOC
class Index extends React.Component{
componentDidMount(){
console.log('———didMounted———')
}
render(){
return <div>hello,world</div>
}
}
HOC还可以对原有组件进行监控。比如对一些事件监控,错误监控,事件监听等一系列操作。
function ClickHoc (Component){
return function Wrap(props){
const dom = useRef(null)
useEffect(()=>{
const handerClick = () => console.log('发生点击事件')
dom.current.addEventListener('click',handerClick)
return () => dom.current.removeEventListener('click',handerClick)
},[])
return <div ref={dom} ><Component {...props} /></div>
}
}
@ClickHoc
class Index extends React.Component{
render(){
return <div className='index' >
<p>hello,world</p>
<button>组件内部点击</button>
</div>
}
}
export default ()=>{
return <div className='box' >
<Index />
<button>组件外部点击</button>
</div>
}
对于属性代理我们虽然不能直接获取组件内的状态,但是我们可以通过ref获取组件实例,获取到组件实例,就可以获取组件的一些状态,或是手动触发一些事件,进一步强化组件,但是注意的是:class声明的有状态组件才有实例,function声明的无状态组件不存在实例。
针对某一种情况, 给组件增加额外的生命周期,我做了一个简单的demo,监听number改变,如果number改变,就自动触发组件的监听函数handerNumberChange。
function Hoc(Component){
return class WrapComponent extends React.Component{
constructor(){
super()
this.node = null
}
UNSAFE_componentWillReceiveProps(nextprops){
if(nextprops.number !== this.props.number ){
this.node.handerNumberChange && this.node.handerNumberChange.call(this.node)
}
}
render(){
return <Component {...this.props} ref={(node) => this.node = node } />
}
}
}
@Hoc
class Index extends React.Component{
handerNumberChange(){
/* 监听 number 改变 */
}
render(){
return <div>hello,world</div>
}
}
对于属性代理HOC:
对于反向代理的HOC:
withRoute: 对于没有被Route包裹的组件,给添加history对象等和路由相关的状态,方便我们在任意组件中,都能够获取路由状态,进行路由跳转。强化props,把Router相关的状态都混入到props中。
function withRouter(Component) {
const displayName = `withRouter(${Component.displayName || Component.name})`;
const C = props => {
/* 获取 */
const { wrappedComponentRef, ...remainingProps } = props;
return (
<RouterContext.Consumer>
{context => {
return (
<Component
{...remainingProps}
{...context}
ref={wrappedComponentRef}
/>
);
}}
</RouterContext.Consumer>
);
};
C.displayName = displayName;
C.WrappedComponent = Component;
/* 继承静态属性 */
return hoistStatics(C, Component);
}
export default withRouter
先从props分离出ref和props, 然后从存放整个route对象上下文RouterContext取出route对象,然后混入到原始组件的props中,最后用hoistStatics继承静态属性。
connect: connect的作用也有合并props, 但是更重要的是接受state, 来控制更新组件。
import store from './redux/store'
import { ReactReduxContext } from './Context'
import { useContext } from 'react'
function connect(mapStateToProps){
/* 第一层: 接收订阅state函数 */
return function wrapWithConnect (WrappedComponent){
/* 第二层:接收原始组件 */
function ConnectFunction(props){
const [ , forceUpdate ] = useState(0)
const { reactReduxForwardedRef ,...wrapperProps } = props
/* 取出Context */
const { store } = useContext(ReactReduxContext)
/* 强化props:合并 store state 和 props */
const trueComponentProps = useMemo(()=>{
/* 只有props或者订阅的state变化,才返回合并后的props */
return selectorFactory(mapStateToProps(store.getState()),wrapperProps)
},[ store , wrapperProps ])
/* 只有 trueComponentProps 改变时候,更新组件。 */
const renderedWrappedComponent = useMemo(
() => (
<WrappedComponent
{...trueComponentProps}
ref={reactReduxForwardedRef}
/>
),
[reactReduxForwardedRef, WrappedComponent, trueComponentProps]
)
useEffect(()=>{
/* 订阅更新 */
const checkUpdate = () => forceUpdate(new Date().getTime())
store.subscribe( checkUpdate )
},[ store ])
return renderedWrappedComponent
}
/* React.memo 包裹 */
const Connect = React.memo(ConnectFunction)
/* 处理hoc,获取ref问题 */
if(forwardRef){
const forwarded = React.forwardRef(function forwardConnectRef( props,ref) {
return <Connect {...props} reactReduxForwardedRef={ref} reactReduxForwardedRef={ref} />
})
return hoistStatics(forwarded, WrappedComponent)
}
/* 继承静态属性 */
return hoistStatics(Connect,WrappedComponent)
}
}
export default Index
第一层接受订阅函数,第二层接收原始组件,然后用forwardRef处理ref,用hoistStatics 处理静态属性的继承,在包装组件内部,合并props,useMemo缓存原始组件,只有合并后的props发生变化,才更新组件,然后在useEffect内部通过store.subscribe()订阅更新。
缓存生命周期 keepaliveLifeCycle: actived 作为缓存路由组件激活时候用,初始化的时候会默认执行一次 , unActived 作为路由组件缓存完成后调用。但是生命周期需要用一个 HOC 组件keepaliveLifeCycle 包裹。
import React from 'react'
import { keepaliveLifeCycle } from 'react-keepalive-router'
@keepaliveLifeCycle
class index extends React.Component<any,any>{
state={
activedNumber:0,
unActivedNumber:0
}
actived(){
this.setState({
activedNumber:this.state.activedNumber + 1
})
}
unActived(){
this.setState({
unActivedNumber:this.state.unActivedNumber + 1
})
}
render(){
const { activedNumber , unActivedNumber } = this.state
return <div style={{ marginTop :'50px' }} >
<div> 页面 actived 次数: {activedNumber} </div>
<div> 页面 unActived 次数:{unActivedNumber} </div>
</div>
}
}
export default index
import {lifeCycles} from '../core/keeper'
import hoistNonReactStatic from 'hoist-non-react-statics'
function keepaliveLifeCycle(Component) {
class Hoc extends React.Component {
cur = null
handerLifeCycle = type => {
if (!this.cur) return
const lifeCycleFunc = this.cur[type]
isFuntion(lifeCycleFunc) && lifeCycleFunc.call(this.cur)
}
componentDidMount() {
const {cacheId} = this.props
cacheId && (lifeCycles[cacheId] = this.handerLifeCycle)
}
componentWillUnmount() {
const {cacheId} = this.props
delete lifeCycles[cacheId]
}
render=() => <Component {...this.props} ref={cur => (this.cur = cur)}/>
}
return hoistNonReactStatic(Hoc,Component)
}
keepaliveLifeCycle: 通过ref或获取 class 组件的实例,在 hoc 初始化时候进行生命周期的绑定, 在 hoc 销毁阶段,对生命周期进行解绑, 然后交给keeper统一调度,keeper通过调用实例下面的生命周期函数,来实现缓存生命周期功能的。
function HOC (Component){
const proDidMount = Component.prototype.componentDidMount
Component.prototype.componentDidMount = function(){
console.log('劫持生命周期:componentDidMount')
proDidMount.call(this)
}
return Component
}
这样做会产生一些不良后果。比如如果你再用另一个同样会修改 componentDidMount 的 HOC 增强它,那么前面的 HOC 就会失效!同时,这个 HOC 也无法应用于没有生命周期的函数组件。
我们可以手动将原始组件的静态方法copy到 hoc组件上来,但前提是必须准确知道应该拷贝哪些方法。
function HOC(Component) {
class WrappedComponent extends React.Component {
/*...*/
}
// 必须准确知道应该拷贝哪些方法
WrappedComponent.staticMethod = Component.staticMethod
return WrappedComponent
}
原生组件的静态方法是未知的,我们可以使用 hoist-non-react-statics 自动拷贝所有的静态方法。
import hoistNonReactStatic from 'hoist-non-react-statics'
function HOC(Component) {
class WrappedComponent extends React.Component {
/*...*/
}
hoistNonReactStatic(WrappedComponent,Component)
return WrappedComponent
}
高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。我们可以通过forwardRef来解决这个问题。
function HOC(Component,isRef){
class Wrap extends React.Component{
render(){
const { forwardedRef ,...otherprops } = this.props
return <Component ref={forwardedRef} {...otherprops} />
}
}
if(isRef){
return React.forwardRef((props,ref)=> <Wrap forwardedRef={ref} {...props} /> )
}
return Wrap
}
class Index extends React.Component{
componentDidMount(){
console.log(666)
}
render(){
return <div>hello,world</div>
}
}
const HocIndex = HOC(Index,true)
export default ()=>{
const node = useRef(null)
useEffect(()=>{
/* 就可以跨层级,捕获到 Index 组件的实例了 */
console.log(node.current.componentDidMount)
},[])
return <div><HocIndex ref={node} /></div>
}
class Index extends React.Component{
render(){
const WrapHome = HOC(Home)
return <WrapHome />
}
}
因为每一次HOC都会返回一个新的WrapHome,react diff会判定两次不是同一个组件,那么每次Index 组件 render触发,WrapHome,会重新挂载,状态会全都丢失。
const WrapHome = HOC(Home)
class index extends React.Component{
render(){
return <WrapHome />
}
}