携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 13 天,点击查看活动详情
大家好,我是爱吃鱼的桶哥Z,今天我们继续来学习在React中开发自定义Hook。
在我们日常的开发中经常会用到setInterval、setTimeout这些定时器的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的学习和实用。
最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家