1. react高阶组件
1.1 高阶组件的概念
高阶组件(Higher Order Component,简称:HOC ): 是 React 中用于重用组件逻辑的高级技术, 它本身不是react中的组件, 而是一个函数, 这个函数接受一个react组件作为参数,并返回一个新组件, 实现了对原有组件的增强和优化, 可以对原有组件中的state, props和逻辑执行增删改操作, 一般用于代码重用和组件增强优化
概述:高阶组件是一个函数,而不是组件,但其参数和返回值都是组件
1.2 高阶组件的使用场景
1.2.1 需要代码重用时
react如果有多个组件都用到同一端逻辑,这是就可以把共同的逻辑部分提取出来,利用高阶组件的形式将这段逻辑整合到每一个组件中,从而减少代码的逻辑重复。
1.2.2 需要组件增强优化时
比如我们在项目中使用的组件有些不是自己写的,而是从网上撸下来的,但是第三方写的组件可能比较复杂,有时不能完全满足需求,且第三方组件不易修改,此时也可以用高阶组件。在不修改原始组件的前提下,对组件添加满足实际开发需求的功能。
一般来说,高阶组件在的父组件,会对原始组件模板做增强优化
return (
)
1.3 高阶组件的实现方式
1.3.1 属性代理
流程:通过创建新组件来包裹原始组件,把原始组件作为新组件的子组件渲染
功能:可实现对原始组件的
props数据更新和组件模板更新
示例代码如下:
import React, { Component } from 'react' function myHoc(OldCom){ return class NewCom extends Component { constructor(props){ super(props); console.log(props); // 路由信息会传入高阶组件的props中,可以对它进行增删改 } render() { return ( // 如果返回一个新模板,相当于把原始组件直接替换了,这就是渲染劫持 return
这是订单页的高阶组件
// 如果要返回原始组件模板,把原始组件作为子组件返回即可 // return
) } } } export default myHoc // 导出高阶组件这个函数
注意:在构造器中路由信息会传入高阶组件的props中,可以对它进行增删改
由于props是只读的,不能修改,要对其进行深复制,然后再进行修改
有两种深复制方式如下:
// this.tempProps = JSON.parse(JSON.stringify(props))
- 第一种深复制会丢失函数,因为JSON里面不识别函数,所以不能放函数
this.tempProps = {...props}
- 第二种深复制不会丢失函数,但只能深复制第一层。
此处使用第二种方式,对props进行增删改操作:
- 增
this.tempProps.name = "张三" console.log(this.tempProps);
- 删
delete this.tempProps.match // ES5 删除属性 var { location, ...tempObj} = this.tempProps // ES6 解构删除 this.tempProps = tempObj console.log(this.tempProps);
- 改
this.tempProps.history.action = "PUSH"
在这里只能操作props,不能操作state,无法调用原始组件的state,如果需要修改state数据,请使用 反向继承 实现
在render()渲染函数中,如果需要修改props,需要传入修改后的深复制品
return (
)
在高阶组件中还可以做登录验证,类似与vue中的路由守卫。
问题:由于路由中配置的是高阶组件,所以路由信息传入到高阶组件中,原始组件中就没有路由信息了,怎么解决?
- 因为此时原始组件中已经没有路由信息了,即没有登录状态,可通过登录状态做一个if判断。若无登录状态,则返回空,并在100秒后跳转到登录页,若有登录状态,则返回原始组件模板页面。
console.log("orderHoc", this.props); if(this.isLogin){ return
}else{ setTimeout(()=>{ this.props.history.push("/login") }, 100) return "" }
- 此时需要在render()函数的返回的原始组件中,动态拼接this.props即可传入路由信息
return (
)
1.3.2 反向继承
流程:通过创建新组件继承自原始组件类,把新组件作为子类,原始组件作为父类
功能:可实现对原始组件的
state状态数据更新和组件模板更新注意:反向继承中,新组件和原始组件要求必须都是类组件
示例代码如下:
import React, { Component } from 'react' function myHoc(OldCom){ return class NewCom extends OldCom{ constructor(props){ super(props); // 因为当前组件类继承于原始组件类,子类可以直接调用父类的数据 console.log("myHoc2", this.state, this.add); // this.setState({ // 此时未能渲染数据 // count: 100 // }) } render(){ return
{super.render()}
} } } export default myHoc
反向继承中,不能返回原始组件标签,因为原始组件是父类,不能作为子组件渲染
return
// 错误写法
需要使用super调用父类的render渲染函数,渲染父类模板
return super.render()
需要在组件渲染之后更新state数据
componentDidMount = () => { this.setState({ count: 100, age: 20 }, ()=>{ this.add() }) }
- this.setState()为异步更新,可在其回调的箭头函数中调用函数
- 而在构造器中只能调用读取state数据,不能调用setState更新
1.4 高阶组件的渲染劫持
高阶组件的渲染劫持:通过高阶组件把原始组件的模板进行修改和替换
1.4.1 渲染劫持概念
渲染劫持指对一个组件渲染内容的装饰或修改, 一般通过高阶组件来实现, 把一个组件传入高阶组件, 可以对这个组件的模板进行修改后执行渲染, 也可以阻止组件渲染, 并修改组件中的数据和逻辑
1.4.2 渲染劫持的应用
一般用于一些需要登录状态的页面, 在路由请求渲染页面(如订单页)之前, 使用高阶组件判断是否已登录, 如果已登录, 返回订单页模板, 如果没有登录, 返回空, 并跳转到登录页
1.5 高阶组件的实现步骤
1.5.1 新建高阶组件文件 MyHOC.jsx
1.5.2 在文件中创建函数
- 函数的参数是一个组件OldCom, 函数的返回值也是一个组件 NewCom
function myHoc(OldCom){ return class NewCom extends React.Component{ render(){ let newProps = { age: 10, sex: '男' } return (
) } } }
属性代理(上)或者(反向继承)
function myHoc (OldCom){ return class NewCom extends OldCom{ componentDidMount() { this.setState({ name: '李四' }) } render() { return super.render() } } }
1.5.3 导出高阶组件函数
export default myHoc
1.5.4 在需要使用高阶组件的原始组件中导入
import MyHOC1 from "./OrderHOC1" // 导入属性代理高阶组件 import MyHOC2 from "./OrderHOC2" // 导入反向继承高阶组件
1.5.5 在导出组件时,使用高阶组件处理之后,再导出
export default MyHOC1(Order) // 属性代理高阶组件处理 // export default MyHOC2(Order) // 反向继承高阶组件处理
原始组件示例代码:
// 这是定义原始组件的页面 import React, { Component } from 'react' import MyHOC1 from "./OrderHOC1" import MyHOC2 from "./OrderHOC2" class Order extends Component { state = { count: 0 } add = ()=>{ this.setState({ count: this.state.count + 1 }) } render() { console.log("order", this.state, this.props); return (
订单页
{this.state.count}
) } } export default MyHOC1(Order) // export default MyHOC2(Order)
2. hooks
hooks语法只适用于函数式组件中,不能用于类组件
2.1 什么是hooks?
hooks是react新版本提供的组合式API语法,类似于vue3组合式API(也叫hooks)
2.2 hooks有什么用?
使函数式组件拥有组件状态和生命周期功能
提供运行效率
避免this指向问题(可以和vue3一样)
2.3 在函数式组件中, 使用hooks语法模拟状态数据的步骤
2.3.1 从react中导入语法函数useState
import React, { useState } from "react"
2.3.2 在函数式组件中, 使用useState创建状态数据
使用useState创建状态数据,参数是默认值,返回值是 数组
数组中第一个值是状态数据的变量名,第二个值是自定义的更新函数,调用更新函数会刷新视图
有三种类型:
- 直接定义单个变量
const [count, setCount] = useState(100)
- 定义多个变量使用对象结构
const [state, setState] = useState({ age: 10, name: "张三", roomList: [] })
- 定义数组
const [arr, setArr] = useState([1, 2, 3])
2.3.3 在组件模板中, 直接调用状态名即可{count}
2.3.4 使用useState函数返回的更新函数,修改状态值setCount(count + 1)
- 调用更新函数,更新状态,参数是最新值,修改后自动刷新界面
- 渲染单数据的写法
- 渲染对象数据的写法
- 修改对象中的状态数据时可以先修改,再拼接上对应的状态名
- 使用useState渲染数组中数据的写法
- 注意:useState定义的引用类型数据,更新时,需要修改数据的内存地址(深拷贝/深复制),才会更新视图
2.4 使用hooks中的useEffect函数实现函数式组件的生命周期
默认第一个参数是回调函数,当组件初始化完成和状态更新时调用,类似于类组件中的render()渲染函数。
第二个参数可以控制回调的调用时机,是一个数组,可选,数组中是状态名,指定那些状态值更新会触发回调函数
有三种实现方法:
- 监听所有的数据更新
- 如果不加第二个参数,初始化时调用,任何状态更新都会调用
useEffect(()=>{ console.log("组件初始化,或有状态更新ComponentWillUpdate") })
- 只在初始化时监听调用
- 如果第二个参数是空数组,则只在初始化时调用,状态更新时不会调用
useEffect(()=>{ console.log("组件初始化") }, [])
- []表示只在初始化时调用,相当于生命周期componentDidMount
可以在这里请求数据,如请求斗鱼数据:
axios.get("/api/RoomApi/live", { page: 1 }).then(res=>{ console.log(res.data.data); setState({ age: 25, name: "计科", roomList: res.data.data }) })
注意:请求数据需要写在useEffect函数的回调中,而[]只在初始化时调用,所以不能写在[]中
- 只监听某些数据的更新
- 如果第二个参数数组中有状态名, 则只会在数组中的状态更新时调用
useEffect(()=>{ console.log("组件初始化或count或arr更新") }, [count, arr])
useEffect()函数的第二个参数是数组时,若里边写状态数据名,则是指定哪些数据更新时可执行回调
3.在react路由6.0中封装withRouter高阶组件
react-router6.0版本废弃了组件内的props路由信息( history, location, match ),6.0之后的版本,组件内默认都没有路由信息,包括withRouter高阶组件也被废弃了,需要使用hooks组合式API导入路由信息。
在react-router6.0中所有的组件都没有路由信息,也没有withRouter,需要使用hooks语法引入路由信息(history, location, match),所以我们可以自己封装一个withRouter高阶组件。
3.1 新建自己封装的withRouter函数文件withRouter.jsx
由于路由数据是在props中,所以使用属性代理方式,实现自封装的高阶组件。
3.2 在文件中导入自定义路由信息
import { useHistory, useLocation, useRouteMatch} from 'react-router-dom'
3.3 创建函数式组件,使用hooks语法
function myHoc(OldCom){ return ()=>{ // 返回组件模板 return
} }
3.4 在返回的箭头函数中,使用hooks提供的组合式API,获取路由信息
const history = useHistory() const location = useLocation() const match = useRouteMatch()
3.5 返回组件模板
return
3.6 导出封装的高阶组件函数
export default myHoc
3.7 在根组件App.js中导入自己封装的withrouter高阶组件
当使用了hooks新语法,还想使用类组件,就可以使用自己封装的高阶组件来实现。
import withRouter from "./pages/Hooks/withRouter";
3.8 在导出根组件时,使用封装的withRouter包裹组件即可传入路由信息
export default withRouter(App);
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203871.html原文链接:https://javaforall.net
