React中使用useSWRInfinite(),如何避免时间戳触发重复请求?
解决useSWRInfinite因初始时间戳频繁变化导致重复请求的问题
问题根源
你当前代码中,timestamp在组件每次重渲染时都会重新生成(new Date().toISOString()会返回新的字符串),导致getKey返回的第一页请求Key不断变化。SWR会将不同的Key视为独立请求,因此频繁触发fetcher函数。
解决方案:固定初始时间戳
核心思路是让初始时间戳只在组件挂载时生成一次,避免随重渲染更新。下面提供两种可行实现:
方案一:使用useState初始化一次时间戳
利用useState的初始化函数特性,仅在组件首次挂载时生成初始时间戳:
const useQuestions = () => { const fetcher = async (url: string) => fetch(url).then((res) => res.json()); // 仅在组件挂载时生成一次初始时间戳 const [initialTimestamp] = useState(() => new Date().toISOString()); const getKey = (pageIndex: number, previousPageData: any) => { if (previousPageData && !previousPageData.questions) return null; // 第一页使用固定的初始时间戳 const cursorQuery = `?cursor=${pageIndex ? previousPageData?.from : initialTimestamp}`; if (pageIndex && !previousPageData?.cursor) return null; return `/api/questions${cursorQuery}`; // 修正原代码笔误:fromQuery → cursorQuery }; const { data, size, setSize, error, isLoading, isValidating, mutate } = useSWRInfinite(getKey, fetcher, { initialSize: 1, revalidateAll: true, revalidateFirstPage: false, persistSize: true, }); // 可选:手动刷新第一页的函数 const refreshFirstPage = () => { const newTimestamp = new Date().toISOString(); mutate(`/api/questions?cursor=${newTimestamp}`, undefined, { revalidate: true }); }; return { data, size, setSize, error, isLoading, isValidating, mutate, refreshFirstPage }; };
方案二:使用useMemo缓存时间戳
通过useMemo依赖空数组,确保时间戳仅计算一次:
const useQuestions = () => { const fetcher = async (url: string) => fetch(url).then((res) => res.json()); // 缓存初始时间戳,仅在组件挂载时生成 const initialTimestamp = useMemo(() => new Date().toISOString(), []); const getKey = (pageIndex: number, previousPageData: any) => { if (previousPageData && !previousPageData.questions) return null; const cursorQuery = `?cursor=${pageIndex ? previousPageData?.from : initialTimestamp}`; if (pageIndex && !previousPageData?.cursor) return null; return `/api/questions${cursorQuery}`; }; const { data, size, setSize, error, isLoading, isValidating, mutate } = useSWRInfinite(getKey, fetcher, { initialSize: 1, revalidateAll: true, revalidateFirstPage: false, persistSize: true, }); return { data, size, setSize, error, isLoading, isValidating, mutate }; };
额外说明
- 原代码存在笔误:
return /api/questions${fromQuery}应改为/api/questions${cursorQuery},否则请求URL会出错。 - 如果需要主动刷新第一页数据,可以调用方案一中的
refreshFirstPage函数,生成新的时间戳并触发SWR重验证。
内容的提问来源于stack exchange,提问作者Iftach
相关产品推荐
相关产品推荐

