React组件化-01-React中的高阶组件

235 阅读6分钟

组件设计的目的:主要是为了保证组件功能的单一性

1.高阶组件:

🚀高阶组件本质上是一个函数,如果函数接收一个组件或者多个组件,返回一个新的组件,则当前组件为高阶组件

  • ①属性代理:对 Comp组件 加工后返回 新组件
/* Hoc.js */

import React, {Component} from 'react';

// 🚀 本质上是一个函数,这个函数接收一个组件或者多个组件,返回一个新的组件,则当前组件为高阶组件
const highOrderCom = (Comp)=>{
    //返回一个新组件
    const NewComponent = (props)=>{
        //🚀 attr为属性代理
        const attr = {type:"高阶组件",info:"2001赛季NBA常规赛MVP-艾弗森"}
        return <Comp {...props} {...attr} ></Comp>
    }
    return NewComponent
}

class Hoc extends Component {
    render() {
        return (
            <div>
                <h3>{this.props.type}</h3>
                <h3>{this.props.info}</h3>
            </div>
        );
    }
}

export default highOrderCom(Hoc);
  • ②重写组件生命周期:

    import React, {Component} from 'react';
    
    // 🚀 本质上是一个函数,这个函数接收一个组件或者多个组件,返回一个新的组件,则当前组件为高阶组件
    
    //🚀②重写生命周期
    const highOrderCom = (Comp)=>{
        //返回一个新组件
        return class extends Component{
            constructor(props) {
                super(props);
            }
            componentDidMount() {
                console.log("共有的发起ajax请求")
            }
            render(){
                return (
                    <Comp {...this.props} person={"艾弗森"} record={"连续四届得分王"}></Comp>
                )
            }
        }
    }
    class Hoc extends Component {
        render() {
            return (
                <div>
                    <h3>{this.props.person}</h3>
                    <h3>{this.props.record}</h3>
                </div>
            );
        }
    }
    
    export default highOrderCom(Hoc);
    

    思考小结①:

🌵为什么我们需要高阶组件?

  • react高阶组件能够让我们写出易于维护的react代码,能早点下班

🌵高阶组件是什么?

  • 本质上是一个函数,函数接收一个组件或者多个组件,返回一个新的组件,则当前组件为高阶组件

  • 比如给你一个赛亚人,完成了你又给我一个超级赛亚人 y = kx + b

  • x好比是普通的组件,k和b就是当前普通组件定制的属性和方法,y就是返回的新组件

🌵如何实现高阶组件?

  • 1.属性代理是最常见的实现方式:

    🚀 好处:常用的方法独立并多次复用

  • 2.反向继承

🌵高阶组件的应用:

  • 代码复用

  • 权限的控制

  • 打印日志

2.高阶组件的应用:

2.1-打印日志的高阶组件的实现:

import React, {Component} from 'react';

//2.8 打印日志的高阶组件实现和链式调用
const withLog = (Comp)=>{
    console.log(Comp.name+"渲染了");  
    const NewComp =  (props)=>{
        return <Comp {...props}></Comp>
    }
    return NewComp;
}

// 🚀 本质上是一个函数,这个函数接收一个组件或者多个组件,返回一个新的组件,则当前组件为高阶组件
//🚀 ②重写生命周期:
const highOrderCom = (Comp)=>{
    //返回一个新组件
    return class extends Component{
        constructor(props) {
            super(props);
            this.handleClick = this.handleClick.bind(this);
        }
        componentDidMount() {
            console.log("共有的发起ajax请求")
        }
        handleClick(){

        }
        render(){
            return (
                <Comp {...this.props} person={"艾弗森"} record={"连续四届得分王"} onClick={this.handleClick}></Comp>
            )
        }
    }
}

class Hoc extends Component {
    render() {
        return (
            <div>
                <h3>{this.props.person}</h3>
                <h3>{this.props.record}</h3>
            </div>
        );
    }
}

// 🚀链式调用
export default highOrderCom(withLog(withLog(Hoc))); 

//🚀 打印结果:
//Hoc渲染了
//NewComp渲染了
//所以是最层的withLog先执行的
  • 🚀控制台会打印出
    • Hoc渲染了
    • NewComp渲染了
    • 共有的发起ajax请求

上面的链式操作还是有多些蛋疼的,一层层的由内往外执行,由此可以引入ES7装饰器写法实现高阶组件调用👇

🚀首先需要安装如下两个依赖包:

  • yarn add craco-less 下载 less ,支持 less 格式文件

  • yarn add @babel/plugin-proposal-decorators 下载支持装饰器使用的模块

  • 然后修改项目目录下的craco.config.js文件:

// craco.config.js

const CracoLessPlugin = require('craco-less');
module.exports = {
    plugins: [
        {
            plugin: CracoLessPlugin,
            options: {
                lessLoaderOptions: {
                    lessOptions: {
                        modifyVars: { '@primary-color': 'red' },
                        javascriptEnabled: true,
                    },
                },
            },
        },
    ],
    babel:{
        plugins: [
          	//🚀用来支持装饰器
          	[["@babel/plugin-proposal-decorators", { legacy: true }],
            [
                "import",
                {
                    "libraryName": "antd",
                    "libraryDirectory": "es",
                    "style": true //设置为true即是less
                }
            ]
        ]
    },
}
  • 装饰器实现高阶组件链式调用

    import React, {Component} from 'react';
    
    //2.8 打印日志的高阶组件实现和链式调用
    const withLog = (Comp)=>{
        console.log(Comp.name+"渲染了");
        const NewComp =  (props)=>{
            return <Comp {...props}></Comp>
        }
        return NewComp;
    }
    
    
    // 🚀 本质上是一个函数,这个函数接收一个组件或者多个组件,返回一个新的组件,则当前组件为高阶组件
    //🚀①属性代理:对`Comp组件`加工后返回`新组件`
    // const highOrderCom = (Comp)=>{
    //     //返回一个新组件
    //     const NewComponent = (props)=>{
    //         //🚀 attr为属性代理
    //         const attr = {type:"高阶组件",info:"2001赛季NBA常规赛MVP-艾弗森"}
    //         return <Comp {...props} {...attr} ></Comp>
    //     }
    //     return NewComponent
    // }
    //🚀②重写生命周期
    const highOrderCom = (Comp)=>{
        //返回一个新组件
        return class extends Component{
            constructor(props) {
                super(props);
                this.handleClick = this.handleClick.bind(this);
            }
            componentDidMount() {
                console.log("共有的发起ajax请求")
            }
            handleClick(){
    
            }
            render(){
                return (
                    <Comp {...this.props} person={"艾弗森"} record={"连续四届得分王"} onClick={this.handleClick}></Comp>
                )
            }
        }
    }
    /*
    🚀 这里使用@符+名字从而完成了高阶组件的链式调用
     */
    @highOrderCom
    @withLog
    @withLog
    class Hoc extends Component {
        render() {
            return (
                <div>
                    <h3>{this.props.person}</h3>
                    <h3>{this.props.record}</h3>
                </div>
            );
        }
    }
    
    // 链式调用
    // export default highOrderCom(withLog(withLog(Hoc)));
    export default Hoc
    
    

上面代码中三个艾特符起到的作用其实等价于 export default highOrderCom(withLog(withLog(Hoc)));

  • @highOrderCom
    @withLog      =   export default highOrderCom(withLog(withLog(Hoc)));
    @withLog
    

2.2高阶组件应用-页面复用:

  • MovieList为视频列表展示页面,MovieAMovieB中都会用到
  • MovieA为电视剧组件
  • MoiveB为动漫组件
  • withFetching为高级组件(非常重要):用来获取数据,对状态赋值然后对传递出去
    • 因为MovieAMovieB对于MovieList而言,逻辑是一样的,所以没必要在AB两个组件中单独请求,通过withFetching来完成对相同逻辑的复用

🌵代码如下:

//MoiveA.js
import React, {Component} from 'react';
import MovieList from "./MovieList";
import {withFetching} from "../HOC/WithFetch";

@withFetching("A电视剧")
class MovieA extends Component {
    // constructor(props) {
    //     super(props);
    //     this.state = {
    //         movies:[]
    //     }
    // }
    // componentDidMount() {
    //     //发起ajax请求
    //     this.setState({
    //
    //     })
    // }
    render() {
        return (
            <MovieList movies = {this.props.datas}> </MovieList>
        );
    }
}
export default MovieA
//MovieB.js
import React, {Component} from 'react';
import MovieList from "./MovieList";
import {withFetching} from "../HOC/WithFetch";

@withFetching("B动漫")
class MovieB extends Component {
    // constructor(props) {
    //     super(props);
    //     this.state = {
    //         movies:[]
    //     }
    // }
    // componentDidMount() {
    //     //发起ajax请求
    //     this.setState({
    //
    //     })
    // }
    render() {
        return (
            <MovieList movies = {this.props.datas}>

            </MovieList>
        );
    }
}
export default MovieB
//MovieList.js

import React, {Component} from 'react';

class MovieList extends Component {
    render() {
        return (
            <ul>
                {
                    this.props.movies.map((item,index)=>{
                        return (<li key={index}>{item.title}--{item.category}</li>)
                    })
                }
            </ul>
        );
    }
}
export default MovieList
//WithFetch.js

import React, {Component} from 'react';
//WithFetch是一个高阶组件
// 🚀用来获取数据 对状态赋值 属性传递

export const withFetching = (fetch) => (Comp)=>{
    return class extends Component{
        constructor(props) {
            super(props);
            this.state = {
                data:[]
            }
        }
        componentDidMount() {
            //获取数据
            if(fetch === "A电视剧"){
                this.setState({
                    data:[
                        {
                            id:1,
                            title:"深夜食堂第一季",
                            category:"A"
                        },
                        {
                            id:2,
                            title:"深夜食堂第二季",
                            category:"A"
                        },
                        {
                            id:3,
                            title:"深夜食堂第三季",
                            category:"A"
                        },
                    ]
                })
            }else{
                this.setState({
                    data:[
                        {
                            id:1,
                            title:"一拳超人第一季",
                            category:"B"
                        },
                        {
                            id:2,
                            title:"一拳超人第二季",
                            category:"B"
                        },
                        {
                            id:3,
                            title:"一拳超人第三季",
                            category:"B"
                        },
                    ]
                })
            }
        }
        render() {
            return (
                <Comp {...this.props} datas = {this.state.data}></Comp>
            )
        }
    }
}

🚀数据流向:

  • 装饰器 ---->
    • 获取到响应的数据A或者B ---->
      • AB组件通过this.props.datas获取到数据 ---->
        • 将this.props.datas通过movies属性传递给MoveList列表组件进行渲染

2.3权限控制

权限控制的例子和 2.2 的类似,这里直接贴代码,不做过多赘述了。

//PageA.js
import React, {Component} from 'react';
import {withAdmin} from "../HOC/withAdmin";

@withAdmin("用户A")
class PageA extends Component {
    render() {
        return (
            <div>
                <h2>PageA页面的内容</h2>
            </div>
        );
    }
}
export default PageA;
//PageB.js
import React, {Component} from 'react';
import {withAdmin} from "../HOC/withAdmin";

@withAdmin("用户B")
class PageB extends Component {
    render() {
        return (
            <div>
                <h2>PageB页面的内容</h2>
            </div>
        );
    }
}

export default PageB;
//withAdmin.js
import React, {Component} from "react";

export const withAdmin =(role)=>(Comp)=>{
    return class extends Component{
        constructor(props) {
            super(props);
            this.state = {
                isAdmin:false //默认是false不可访问
            }
        }
        componentDidMount() {
            // 已经从后端获取到该页面权限
            const currentP = "用户A";
            this.setState({
                isAdmin:currentP === role
            })
        }

        render(){
            if(this.state.isAdmin){
                return <Comp {...this.props}> </Comp>
            }else{
                return <div> 您没有查看该页面的权限,请联系管理员进行添加。</div>
            }
        }
    }
}