含义
从标题上看,自定义hook的主要是自己定义,那么对于hooks的定义又是啥呢? 简单点的回答,hooks是一个函数,并且是在react 函数组件中使用的,不同的hook的作用也是不一样的,例如,state hook是用来定义函数组件的状态, 而effect hook 是用来定义组件的副作用,那么自定义hook是用来干啥的呢?,自定来定义一个hook 函数,里面可以包含 多个hooks。简单点的说是,把相同逻辑的hooks封装在同一个函数里。
规则
1.hooks 的使用方式都是 use 开头,那么我们自己定义的也用这个use 开头,作为是一个hook 的标记,统一开发规范
2.自定义的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 就比较纯粹,组件结果不增加,做到真正的横向关注点。