组件设计的目的:主要是为了保证组件功能的单一性
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
为视频列表展示页面,MovieA
和MovieB
中都会用到MovieA为
电视剧组件MoiveB
为动漫组件withFetching
为高级组件(非常重要):用来获取数据,对状态赋值然后对传递出去- 因为
MovieA
和MovieB
对于MovieList
而言,逻辑是一样的,所以没必要在A
、B
两个组件中单独请求,通过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列表组件进行渲染
- AB组件通过this.props.datas获取到数据 ---->
- 获取到响应的数据A或者B ---->
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>
}
}
}
}