官方文档对 useIntervel 的描述是:一个可以处理 setInterval 的 Hook。
在官方示例中展示的useInterval 的功能,可以概括为以下 3 条:
- 支持定时功能
- 定时器运行过程中,时间间隔可修改
- 定时器可以清除
首先我们先使用 hook 实现一下满足这 3 条功能要求的定时器,代码如下:
const useInterval = (fn: () => void, delay?: number) => {
const timerCallback = fn;
const timerRef = useRef(null);
const clear = useCallback(() => {
if (timerRef.current) {
clearInterval(timerRef.current);
}
}, []);
useEffect(() => {
timerRef.current = setInterval(timerCallback, delay);
return clear;
}, [delay]);
return clear;
};
export default useInterval;
使用 useRef 保存 setInterval 的 id,可以在多次渲染后持续访问这个 id。
将 clear 函数作为返回值,暴露给调用者。
现在已经支持了完整的定时器功能。
再增加不影响主逻辑的补充内容:
- useRef 类型定义
- 对于 delay 的数据校验
- 对入参中函数的缓存处理
- 标识回调函数是否立即执行的入参以及对应的逻辑判定
添加这四项之后,就完成了 ahooks 源码中的 useInterval。
useInterval 中会用到两个 ahooks 内部的工具函数, useMemoizedFn 和 isNumber,他们的用法如下:
useMemoizedFn:持久化 function 的 Hook,一般情况下,可以使用 useMemoizedFn 完全代替 useCallback。
isNumber:用于判定某个变量是否是数字类型。
useInterval 的完整实现代码如下:
// 引入 React 基本 hook
import { useCallback, useEffect, useRef } from 'react';
import useMemoizedFn from '../useMemoizedFn';
import { isNumber } from '../utils';
const useInterval = (fn: () => void, delay?: number, options: { immediate?: boolean } = {}) => {
// 缓存入参中的函数
const timerCallback = useMemoizedFn(fn);
// 用 ReturnType 获取 interval 的类型
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
const clear = useCallback(() => {
if (timerRef.current) {
clearInterval(timerRef.current);
}
}, []);
useEffect(() => {
// 对 delay 数据的校验
if (!isNumber(delay) || delay < 0) {
return;
}
// 是否立即执行一次回调函数
if (options.immediate) {
timerCallback();
}
timerRef.current = setInterval(timerCallback, delay);
return clear;
}, [delay, options.immediate]);
return clear;
};
export default useInterval;