一、使用requestAnimationFrame实现
这段代码是一个自定义的 React Hook 函数 usePolling,用于实现轮询从服务器获取数据的功能。它使用 requestAnimationFrame 来控制轮询的时间间隔,以保持稳定的间隔时间。
代码的主要部分:
-
interface PollingOptions<T>:这是一个泛型接口,用于指定轮询的配置选项。其中包括reqFunction,一个从服务器获取数据的异步函数,以及interval,轮询的间隔时间(以毫秒为单位)。 -
usePolling:这是自定义的 React Hook 函数,它接受一个对象参数PollingOptions<T>并返回一个状态变量data。 -
useState:这是 React 的状态钩子函数,用于在组件中存储状态数据。在这里,data是存储轮询获取到的数据的状态变量。 -
useRef:这是 React 的引用钩子函数,用于在组件之间存储可变的值。在这里,animationFrameRef是用于存储requestAnimationFrame的引用,startTimeRef用于存储轮询开始的时间戳。 -
startPolling:这是一个异步函数,用于发起轮询过程。它通过requestAnimationFrame来控制轮询的时间间隔,从而实现稳定的间隔时间。在每次轮询时,它会检查是否达到了轮询的间隔时间,如果是,则调用reqFunction来从服务器获取数据,并将数据存储在data状态变量中。然后,它会重置开始时间,并继续设置下一次轮询。 -
useEffect:这是 React 的副作用钩子函数,用于在组件挂载和更新时执行副作用操作。在这里,它在组件挂载时调用startPolling,开始轮询过程。另外,它还在组件卸载时清除动画帧,以避免内存泄漏。
总结:这个自定义的 Hook 函数 usePolling 可以在 React 组件中使用,通过传入 reqFunction 和 interval 来设置轮询的配置。它使用 requestAnimationFrame 来控制轮询的时间间隔,从而保持稳定的轮询间隔。每次轮询时,它会从服务器获取最新数据,并将其存储在 data 状态变量中供组件使用。
import { useEffect, useRef, useState } from 'react';
interface PollingOptions<T> {
reqFunction: () => Promise<T>; // 从服务器获取数据的函数
interval: number; // 轮询的间隔时间(毫秒)
}
function usePolling<T>({ reqFunction, interval }: PollingOptions<T>) {
const [data, setData] = useState<T | null>(null);
const animationFrameRef = useRef<number | null>(null);
const startTimeRef = useRef<number>(0);
// 开始轮询的函数
const startPolling = async (timestamp: number) => {
if (!startTimeRef.current) {
startTimeRef.current = timestamp;
}
// 检查是否达到了轮询的间隔时间
if (timestamp - startTimeRef.current >= interval) {
try {
const response = await reqFunction();
setData(response);
} catch (error) {
// 处理错误
console.error('轮询发生错误:', error);
}
// 重置开始时间
startTimeRef.current = timestamp;
}
// 继续下一次轮询
animationFrameRef.current = requestAnimationFrame(startPolling);
};
// 在组件挂载时开始轮询
useEffect(() => {
// 开始第一次轮询
animationFrameRef.current = requestAnimationFrame(startPolling);
// 组件卸载时清除动画帧
return () => {
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
};
}, [reqFunction, interval]);
return data;
}
export default usePolling;
二、使用setTimeout实现
使用setTimeout实现轮询与上述大同小异,让我们来解释代码的主要部分:
useEffect:在这里,它在组件挂载时设置一个定时器setTimeout,在指定的interval时间后执行startPolling函数。这样就实现了每隔一段时间就从服务器获取一次最新数据。另外,它还在组件卸载时清除定时器,以避免内存泄漏。
总结:这个自定义的 Hook 函数 usePolling 使用 setTimeout 来实现轮询,每隔指定的 interval 时间就从服务器获取最新数据,并将其存储在 data 状态变量中供组件使用。在组件卸载时,会清除定时器,确保不会出现内存泄漏的问题。
import { useEffect, useRef, useState } from 'react';
interface PollingOptions<T> {
reqFunction: () => Promise<T>; // 从服务器获取数据的函数
interval: number; // 轮询的间隔时间(毫秒)
}
function usePolling<T>({ reqFunction, interval }: PollingOptions<T>) {
const [data, setData] = useState<T | null>(null);
const timeoutRef = useRef<number | null>(null);
// 开始轮询的函数
const startPolling = async () => {
try {
const response = await reqFunction();
setData(response);
} catch (error) {
// 处理错误
console.error('轮询发生错误:', error);
}
};
// 在组件挂载时开始轮询
useEffect(() => {
timeoutRef.current = window.setTimeout(startPolling, interval);
// 组件卸载时清除定时器
return () => {
if (timeoutRef.current) {
window.clearTimeout(timeoutRef.current);
}
};
}, [reqFunction, interval]);
return data;
}
export default usePolling;
三、使用setInterval实现
使用 setInterval 来控制轮询的时间间隔,以实现轮询:
-
usePolling:这是自定义的 React Hook 函数,它接受一个对象参数PollingOptions<T>并返回一个状态对象{ data, setData }。 -
useState:这是 React 的状态钩子函数,用于在组件中存储状态数据。在这里,data是存储轮询获取到的数据的状态变量,setData是用于更新data的状态更新函数。 -
useRef:这是 React 的引用钩子函数,用于在组件之间存储可变的值。在这里,pollingRef是用于存储定时器的引用。 -
useEffect:它在组件挂载时设置一个定时器setInterval,在每隔指定的interval时间后执行startPolling函数。这样就实现了每隔一段时间就从服务器获取一次最新数据,并将其存储在data状态变量中供组件使用。另外,它还在组件卸载时清除定时器,以避免内存泄漏。
总结:这个自定义的 Hook 函数 usePolling 可以在 React 组件中使用,通过传入 reqFunction 和 interval 来设置轮询的配置。它使用 setInterval 来实现轮询,每隔指定的 interval 时间就从服务器获取最新数据,并将其存储在 data 状态变量中供组件使用。在组件卸载时,会清除定时器,确保不会出现内存泄漏的问题。同时,它返回了一个对象 { data, setData },使得组件可以访问轮询获取的数据,并且可以通过 setData 更新数据。
import { useEffect, useRef, useState } from 'react';
interface PollingOptions<T> {
reqFunction: () => Promise<T>; // 从服务器获取数据的函数
interval: number; // 轮询的间隔时间(毫秒)
}
function usePolling<T>({ reqFunction, interval }: PollingOptions<T>) {
const [data, setData] = useState<T | null>(null);
const pollingRef = useRef<number | null>(null);
// 开始轮询的函数
const startPolling = async () => {
try {
const response = await reqFunction();
setData(response);
} catch (error) {
console.error('轮询错误:', error);
}
};
// 组件挂载时立即开始轮询,并设置轮询的间隔时间
useEffect(() => {
pollingRef.current = window.setInterval(startPolling, interval);
return () => {
// 组件卸载时清除定时器
if (pollingRef.current) {
clearInterval(pollingRef.current);
}
};
}, [reqFunction, interval]);
return { data, setData };
}
export default usePolling;
使用方式
import React, { useEffect, useState } from 'react'
import usePolling from '@hook/usePolling';
function App() {
const pollingInterval = 5000; //轮询间隔,单位为豪秒
//轮询队伍状态
const getPollingData = async () => {
const res = await getData({
uuid: uuid,
})
return res
}
const data = usePolling({reqFunction: getPollingData, interval: pollingInterval})
useEffect(() => {
console.log('当前data:', data)
}, [data]);
return (
<div className="app">
{data}
</div>
);
}
export default App;
以上三种实现方式的优劣
以上三种实现轮询的方式分别是:
-
使用
setTimeout方式:在usePolling中使用setTimeout来设置每次轮询的间隔时间。 -
使用
setInterval方式:在usePolling中使用setInterval来设置轮询的间隔时间,使用clearInterval清除定时器。 -
使用
requestAnimationFrame方式:在usePolling中使用requestAnimationFrame来设置每次轮询的间隔时间,使用cancelAnimationFrame清除动画帧。
区别和优劣势:
-
使用
setTimeout方式:优势:
- 简单易用,代码实现相对直接。
- 可以自由地设置每次轮询的间隔时间,灵活性较高。
劣势:
- 由于
setTimeout的精确度有限,可能会导致轮询的间隔时间不稳定,尤其在页面存在其他任务或资源负载较高时。 - 在每次轮询时都需要设置新的
setTimeout,可能导致多次重复的计时器创建。
-
使用
setInterval方式:优势:
- 相比
setTimeout,setInterval可以更稳定地设置轮询的间隔时间,因为它是周期性地触发回调函数。 - 代码相对简单,使用
clearInterval可以轻松清除定时器。
劣势:
- 仍然有一定的时间精度限制,可能会受到页面负载的影响导致轮询不够精确。
- 如果某次轮询任务耗时较长,可能导致下一次轮询被延迟执行,可能不是最佳的选择。
- 相比
-
使用
requestAnimationFrame方式:优势:
requestAnimationFrame的精确度较高,它在浏览器重绘之前执行回调函数,通常为每秒60次(60FPS)。- 适用于需要较高精确度的动画或定时任务,能够避免累积延迟的问题。
- 不会导致页面在后台标签中执行时降低性能,因为它会在页面活跃时才触发回调。
劣势:
requestAnimationFrame的执行时间通常是在16.7毫秒(1秒/60)左右,无法直接设置其他间隔时间。- 如果需要自定义较长的轮询间隔,可能需要在回调函数中自行实现时间控制。
综合来说,如果需要较高精确度和性能较好的定时任务,可以考虑使用 requestAnimationFrame 方式。如果简单的轮询,并且间隔时间不是特别关键,可以使用 setInterval。而 setTimeout 方式则在一些特定场景下使用较多,但需要特别注意间隔时间的不稳定性。