深度学习React高阶组件

SegmentFault

共 8292字,需浏览 17分钟

 ·

2020-09-05 14:25

作者:忘言

来源:SegmentFault 思否社区




混合蛋白


在了解高阶组件之前我们先讲一下mixin很多初级工程师对mixin的概念并不是很了解,首先解释一下mixin


mixin:在项目中有些一码相同的代码会重复使用,我们就可以进行抽离,方便维护,为了解决这个问题就是包装成mixin方法,但是后来发现有mixin有很多缺点端,也许可以说高阶组件就是mixin的衍生品,让我们进入今天的主题





高阶组件


高阶组件:本身是一个函数,这个函数接受一个组件做为参数,并且返回一个组件


实现方式:属性代理和反向继承


示例:


function Hoc(WrappedComponent) {//hoc就是是个高阶组件   接受一个组件做为参数    return class extends Component {//返回一个组件        render() {            return (                

这是一个高阶组件


) } }}


那么高阶组件有什么用?

  • 代码规范
  • 抽离州
  • 操作道具
  • 获取实例
  • 布局更改


接下来我们一个个例子查看它的好处


现在有一个组件ConfirmBox点击按钮后消失


class Dialog extends Component {    constructor(props) {        super(props);        this.state = {            show:true,            msg:'这是一个对话框',            title:'对话框'        }    }    handleConfirm = () => {        this.setState({            show:!this.state.show        })    }    render() {        return (            

{this.state.title}

{this.state.msg}

) }}





属性代理


现在我们使用高阶组件把ConfirmBox组件里的state和回调函数抽离出来
属性代理可以做什么?


  • 抽离州
  • 操作道具
  • 获取实例
  • 布局更改


接下来对props进行操作


function Hoc(WrappedComponent) {//hoc就是是个高阶组件   接受一个组件做为参数    return class extends Component {//返回一个组件        constructor(props) {            super(props);            this.state = {                show:true,                msg:'这是一个对话框',                title:'对话框'            }        }        handleConfirm = () => {            this.setState({                show:!this.state.show            })        }        render() {            return (                

这是一个高阶组件


) } }}class ConfirmBox extends Component { constructor(props) { super(props);
}
render() { return (

{this.props.title}

{this.props.msg}

) }}


现在我们已经把state和回调函数已经抽离到高阶组件中


function Hoc(WrappedComponent) {//hoc就是是个高阶组件   接受一个组件做为参数    return class extends Component {//返回一个组件        constructor(props) {            super(props);            this.state = {                show:true,                msg:'这是一个对话框',                title:'对话框'            }        }        handleConfirm = () => {            this.setState({                show:!this.state.show            })        }        render() {            const newProps = {                show:true,                msg:'这是新的props'            }            return (                

这是一个高阶组件


) } }}class ConfirmBox extends Component { constructor(props) { super(props);
}
render() { return (

{this.props.title}

{this.props.msg}

) }}


到这里不知道会不会有人感觉这个和阵营的UI和逻辑抽离很像,其实是没错的,UI和业务逻辑拆分也类似,为什么那还要有高阶组件呢


这里需要强调地点一下高阶组件的英文函数意味着参数是动态的这一点很重要,如果有两个组件有同样的逻辑通过高阶组件只需要改变实参就可以实现而UI和业务逻辑分解就不行。


假设:现在有一个ConfirmBox2和ComfirmBox1的逻辑一样那我们直接改变参数就可以了


Hoc(ConfirmBox2)


简单一行我们就把逻辑推理给了ComfirmBox2

使用高阶组件相对比较灵活,这里我可能找的例子不好,请各位读者见谅。

通过ref访问组件实例



                        {...newProps} title={this.state.title}                        handleConfirm={this.handleConfirm}                        ref={ref => this.myInstance = ref}>

这样我们就得到了组件实习了





反向继承(不推荐使用)


反向继承:既然有继承两字肯定和extends脱不了干系,反向继承就是通过继承传进来的组件参数并且在render调用super.render实现的


反向继承:可以做什么?


  • 操作状态
  • 渲染劫持


最简单的例子:


function Hoc(WrappedComponent) {//hoc就是是个高阶组件   接受一个组件做为参数    return class extends WrappedComponent {//继承传进来的组件        render() {            return (                super.render();//调用super.render
) } }}


操作state:根据状态的show判断是否显示组件


function Hoc(WrappedComponent) {//hoc就是是个高阶组件   接受一个组件做为参数    return class extends WrappedComponent {//返回一个组件        static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})`        componentWillMount() {            // 可以方便地得到state,做一些更深入的修改。            this.setState({                msg: '这是hoc修改的值'            });        }        render() {            return (                
{ super.render()}


) } }}


渲染劫持

function Hoc(WrappedComponent) {//hoc就是是个高阶组件   接受一个组件做为参数    return class extends WrappedComponent {//返回一个组件        static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})`        componentWillMount() {            // 可以方便地得到state,做一些更深入的修改。            this.setState({                msg: '这是hoc修改的值'            });        }        render() {            if(this.state.show) {                return super.render()            }else {                return null            }        }    }}


反向代理:为什么不推荐使用,因为操作状态容易覆盖,并且生命周期函数也会覆盖

function Hoc(WrappedComponent) {//hoc就是是个高阶组件   接受一个组件做为参数    return class extends WrappedComponent {//返回一个组件        static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})`        componentWillMount() {            // 可以方便地得到state,做一些更深入的修改。            this.setState({                msg: '这是hoc修改的值' //修改了msg的值            });        }        componentDidMount(){            console.log("hoccomponentDidMount")        }        render() {            if(this.state.show) {                return super.render()            }else {                return null            }        }    }}class ConfirmBox extends Component {    constructor(props) {        super(props);        this.state = {            show:true,            msg:'这是一个对话框',            title:'对话框'        }    }    handleConfirm = () => {        this.setState({            show:!this.state.show        })    }    componentDidMount() {        console.log("comfirmbox    componentDidMount")    }    render() {        return (            

{this.state.title}

{this.state.msg}

) }}


从图片可以修剪被包裹组件的componentDidMount生命周期函数组覆盖了,如果要调用需要使用super.componentDidMount去调用,而且状态也被覆盖了另外,最重要的一点反向继承不能保证子组件完全被渲染,这是什么意思?就是被包裹组件里的可能会丢失。





高阶组件使用场景


页面渲染


function Hoc(WrappedComponent) {//hoc就是是个高阶组件   接受一个组件做为参数    return class Test extends Component {//返回一个组件        static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})`        constructor(props) {            super(props);            this.state = {                show:true,                msg:'这是一个对话框',                title:'对话框'            }        }        handleConfirm = () => {            this.setState({                show:!this.state.show            })        }        componentDidMount() {            console.log(this.myInstance)        }
render() { const newProps = { show:true, msg:'这是新的props' } if(this.state.show) { return {...newProps} title={this.state.title} handleConfirm={this.handleConfirm} ref={ref => this.myInstance = ref}> }else { return null }
} }}


逻辑复用:现在假设有两个确认框组件,他们的逻辑一样,只有样式不同,那我们就可以使用高阶组件来实现


import React,{Component} from 'react'
import './Hoc.css'function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数 return class Test extends Component {//返回一个组件 static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})` constructor(props) { super(props); this.state = { show:true, msg:'这是一个对话框', title:'对话框' } } handleConfirm = () => { this.setState({ show:!this.state.show }) } componentDidMount() { console.log(this.myInstance) }
render() { const newProps = { show:true, msg:'这是新的props' } if(this.state.show) { return {...newProps} title={this.state.title} handleConfirm={this.handleConfirm} ref={ref => this.myInstance = ref}> }else { return null }
} }}class ConfirmBox extends Component { constructor(props) { super(props);
}
render() { return (

{this.props.title}

{this.props.msg}

) }}class ConfirmBox2 extends Component { constructor(props) { super(props);
}
render() { return (
{this.props.title}

{this.props.msg}

) }}
export default { comfirmBox:Hoc(ConfirmBox), comfirmBox2:Hoc(ConfirmBox2)}

109b7d945f9b0c65c485d99de7b9d7b8.webp

369d5d91c177320715dbcd983555fbef.webp


可以抛光组件只是样式不同,逻辑相同我们就可以使用高阶组件来实现逻辑替换




点击左下角阅读原文,到 SegmentFault 思否社区 和文章作者展开更多互动和交流。
- END -

35ab2a0745da7559a79bdbfd07b02009.webp

浏览 15
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报