含义
从标题上看,自定义hook的主要是自己定义,那么对于hooks的定义又是啥呢? 简单点的回答,hooks是一个函数,并且是在react 函数组件中使用的,不同的hook的作用也是不一样的,例如,state hook
是用来定义函数组件的状态, 而effect hook
是用来定义组件的副作用,那么自定义hook是用来干啥的呢?,自定来定义一个hook 函数,里面可以包含 多个hooks。简单点的说是,把相同逻辑的hooks
封装在同一个函数里。
规则
- hooks 的使用方式都是
use
开头,那么我们自己定义的也用这个use 开头,作为是一个hook 的标记,统一开发规范 - 自定义的hooks 肯定也是一个函数,并且需要和react 给我们的组件一样,需要放到顶层
作用
自定义hooks的作用让代码更加简洁,就是对代码逻辑块的封装。而hooks的作用主要是横切关注点来处理问题。处理自定义hooks可以处理横切关注点外,还有高阶组件
和props render
也是来做这个事情的。
案例
在这里我们想要实现一个这样的功能,做一个每隔1s来轮询后台的方法,并且在使用一个开关,想要轮询打开就行,不想要直接关闭。下面使用两种方式来实现,一种是自定义hook的方式,另一种是 函数组件的高阶组件也可以实现这样的效果
自定义hook 案例源码
import React, { useEffect, useState } from 'react'
/**
* 列表组件
* @returns
*/
function ListComp() {
const { count, data } = useTimerReqHooks();
const liDom = data.map((it, index) => (<li key={index}>{it}</li>));
return (
<>
<p>次数: {count}</p>
<p>数据</p>
<ul>
{liDom}
</ul>
</>)
}
/**
* 测试组件
* @returns
*/
export default function TestCusHook() {
const [hasShow, setHasShow] = useState(true);
return (
<div>
<p><button onClick={() => { setHasShow(!hasShow) }}>隐藏/显示</button></p>
{hasShow && <ListComp />}
</div>
)
}
/**
* 自定义hook,做一个轮询后台的处理,每隔1s钟发一次请求(实际的请求不可能这么频繁)
*/
export function useTimerReqHooks() {
// 计时器记录的数据
const [count, setCount] = useState<number>(0);
// 时间变化请求
const [data, setData] = useState<number[]>([]);
// 计数器
let timer: number | null = null;
// 副作用,发起请求
useEffect(() => {
timer = setInterval(() => {
// 计时器值 + 1
setCount(pre => pre + 1);
// 这里用一个立即执行函数来发送请求
(async () => {
const res = await getData(1, 10);
console.log(data, res, '-=====');
// 如果想要拿到先前的数据,需要返回一个函数,不然做不到,异步的
setData(pre => [...pre, res]);
})()
}, 1000)
return () => {
// 清空定时器
if (timer) {
clearInterval(timer);
timer = null;
}
}
}, [count]);
return {
data,
count
}
}
/**
* 模拟发送请求,每一次返回一个随机数
*/
function getData(min: number, max: number): Promise<number> {
return new Promise((resolve, reject) => {
resolve(parseInt((Math.random() * (max - min)).toString(), 10) + min)
})
}
自定义hook 效果
组件树是纯粹的,没有添加任何的其他组件,方便调试
高阶组件
import React, { PureComponent } from 'react'
/**
*
* @param comp 高阶组件的接口
* @returns
*/
interface IWithTimerReqS {
// 当前次数
count: number,
// 获取的结果
data: number[]
}
/**
* 显示组件
*/
interface ITestCusCompS extends Partial<IWithTimerReqS> {
// 是否展示组件
hasShow: Boolean,
}
/**
* 高阶组件
*/
function withTimerReq(Comp: React.ComponentClass<IWithTimerReqS>) {
return class withTimerReqs extends PureComponent<{}, IWithTimerReqS> {
state: IWithTimerReqS = {
count: 0,
data: [],
}
private timer: number | null = null;
// 组件初始化的时候进行启动定时器
componentDidMount() {
this.setData();
}
// 数据更新进行操作
componentDidUpdate(prevProps: {}, prevState: IWithTimerReqS) {
this.setData();
}
private setData(){
if(this.timer){
clearInterval(this.timer);
this.timer = null;
}
// 启动定时器
this.timer = setInterval(() => {
// 计时器值 + 1
this.setState(pre => {
return {
...pre,
count: pre.count + 1
}
});
// 这里用一个立即执行函数来发送请求
(async () => {
const res = await getData(1, 10);
console.log(this.state.data, res, '-=====');
this.setState(pre =>{
return {
...pre,
data: [...pre.data, res]
}
});
})()
}, 1000)
}
// 组件卸载,清空定时器
componentWillUnmount() {
if(this.timer){
clearInterval(this.timer);
this.timer = null;
}
}
render() {
return (<>
<Comp {...this.state} />
</>)
}
}
}
/**
* 列表组件
*/
class ListComp extends PureComponent<IWithTimerReqS> {
render(){
const liDom = this.props.data.map((it, index) => (<li key={index}>{it}</li>));
return (
<>
<p>次数: {this.props.count}</p>
<p>数据</p>
<ul>
{liDom}
</ul>
</>
)
}
}
// 使用高阶组件包裹
const WithComp = withTimerReq(ListComp)
export default class TestCusComp extends PureComponent<{}, ITestCusCompS> {
state: ITestCusCompS = {
hasShow: true
}
render() {
return (
<div>
<p><button onClick={() => { this.setState({ hasShow: !this.state.hasShow }) }}>隐藏/显示</button></p>
{this.state.hasShow && <WithComp />}
</div>
)
}
}
/**
* 模拟发送请求,每一次返回一个随机数
*/
function getData(min: number, max: number): Promise<number> {
return new Promise((resolve, reject) => {
resolve(parseInt((Math.random() * (max - min)).toString(), 10) + min)
})
}
高阶组件效果
总结
虽然两者实现的效果都是一样的,但是类组件中的代码量比hooks 多了大概50行左右,而且还比较绕。但是hooks 就比较纯粹,组件结果不增加,做到真正的横向关注点。