快来学习一下如何在React中开发自定义Hook - useInterval

135 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 13 天,点击查看活动详情

大家好,我是爱吃鱼的桶哥Z,今天我们继续来学习在React中开发自定义Hook

在我们日常的开发中经常会用到setIntervalsetTimeout这些定时器的API,而在React中如果要使用setInterval我们需要注意Hook的局限性和一些潜在的问题,下面我们就一起来学习一下如何在React中开发一个自定义Hook - useInterval

setInterval

首先,我们应该明确的是setInterval是有副作用的,因为setInterval这个方法是没有直接与组件的渲染方法进行绑定的。因此,我们需要在useEffect这个Hook中来调用,并且当组件卸载的时候需要通过useEffect的回调函数来清除这个定时器(clearInterval)。下面我们一起看一下最基础的代码是什么样的,如下:

import React, { useEffect } from 'react';

// 伪代码
useEffect(() => {
    const id = setInterval(callback, delay);
    return () => clearInterval(id);
}, []);

上述的代码中,setInterval有两个参数,第一个参数是需要执行的回调函数,第二个参数是执行的间隔时间,单位是毫秒。当组件卸载时,我们在useEffect的回调函数中通过clearInterval来清除这个定时器,它有一个参数,这个参数是前面定义的定时器的id,因为只有知道是哪个定时器需要清理,我们才能使用clearInterval将这个定时器给清理掉。

在这个例子中,setInterval内部的方法只能访问它内部的变量,因为产生了闭包,如果我们需要在定时器中每次都获取到最新的值,就需要借助React中的useRef这个Hook,它能帮我们保存实时变化的数据,但是这个数据是不会通过UI的变化来展现的,具体的API可以查看这里

我们可以将上面的代码进行修改,修改后的代码能够实时获取到最新的值,代码如下:

import React, { useEffect, useRef } from 'react';

const savedCallback = useRef(callback);

// 伪代码
useEffect(() => {
    const id = setInterval(savedCallback?.crrent, delay);
    return () => clearInterval(id);
}, []);

我们通过useRef就可以解决闭包的问题,现在我们还需要在setInterval内部刷新前面创建的ref的值,相对来说,这是一个比较简单的问题。我们可以创建一个函数,将这个函数传递给setInterval,这样我们传递给setIntercal的函数就永远都不会改变了,但是闭包内的ref的值却始终都保持最新。我们一起来看一下代码是如何修改的,如下:

import React, { useEffect, useRef } from 'react';

const savedCallback = useRef(callback);

// 伪代码
useEffect(() => {
    const tick = () => {
        savedCallback?.crrent();
    }
    const id = setInterval(tick, delay);
    return () => clearInterval(id);
}, []);

最后,我们需要将上面的这些代码封装成一个自定义Hook,这样就可以方便我们在其它地方进行复用。我们可以将回调函数作为自定义Hook的参数提取出来,并将它用作useEffect的依赖项,以此来更新ref的值。

让我们一起来实现useInterval这个自定义Hook吧。

useInterval

还记得前面我们是如何开发自定义Hook的吗?我们需要遵循React开发自定义Hook的相关规则,让我们一起来看一下代码的实现,如下:

// useInterval.tsx
import React, { FC, useEffect, useRef } from 'react';

interface UseIntervalProps {
    callback: () => void;
    delay: number
}

const useInterval: FC<UseIntervalProps> = (callback, delay) => {
    const savedCallback = useRef();
    
    // 初始化 savedCallback
    useEffect(() => {
        savedCallback.current = callback;
    }, [callback]);
    
    useEffect(() => {
        const task = () => savedCallback.current();
        
        let id = setInterval(task, delay);
        return () => clearInterval(id);
    }, [delay]);
}

这个自定义Hook就写完了,那么我们要如何使用呢?可以看一下相关的代码,如下:

// 伪代码
const Timer = () => {
    const [seconds, setSeconds] = useState(0);
    useInterval(() => {
        setSeconds(seconds + 1);
    }, 1000);
    
    return <p>{seconds}</p>
} 

具体的执行效果,可以狠戳这里。在页面中,我们每秒执行一次setSeconds,当然这只是实现的一个Demo,我们在真实的开发中还可以基于这个自定义Hook做其它的工作,例如:每10秒轮询一次,查询后端的状态是否已经变更等等。

最后

关于React配合TS实现自定义Hook,还有很多值得我们学习的地方,后续我还会继续给大家分享一些实用的自定义Hook,以此提高我们对React的学习和实用。

最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家

往期回顾

React中这几个好用的Hook你还不会吗?快来学习一下

React中自定义hook,提高开发效率,你学会了吗?

React中这几个常用的自定义Hook,你学会了吗?

React中这几个常用的自定义Hook,你学会了吗?(2)