react 进阶hook 之自定义Hook

含义

从标题上看,自定义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 就比较纯粹,组件结果不增加,做到真正的横向关注点。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

twinkle||cll

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值