React中如何避免列表子组件EventCard重复调用API
解决EventCard重复调用API的问题
首先,你当前的useEffect存在一个小问题:定义了异步函数但没有执行,需要补充调用逻辑,但这不是重复请求的核心原因。核心问题是每个EventCard实例都会独立发起fetchZones请求,导致重复调用。以下是几种符合你需求的解决方案:
1. 给fetchZones添加单例缓存(最直接的方案)
用闭包封装带缓存的请求函数,相同serverId下只会发起一次请求,后续调用直接复用之前的Promise结果:
// 封装带缓存的fetchZones函数 const fetchZonesWithCache = (() => { // 用Map缓存每个serverId对应的请求Promise const requestCache = new Map<string, Promise<Zone[]>>(); return async (serverId: string) => { // 如果已有缓存,直接返回缓存的Promise if (requestCache.has(serverId)) { return requestCache.get(serverId)!; } // 没有缓存则发起请求 const fetchPromise = fetch(`/api/zones?serverId=${serverId}`) .then(res => { if (!res.ok) throw new Error('请求失败'); return res.json(); }) .catch(err => { // 请求失败时删除缓存,允许后续重试 requestCache.delete(serverId); throw err; }); requestCache.set(serverId, fetchPromise); return fetchPromise; }; })(); // 在EventCard中使用 useEffect(() => { const loadZones = async () => { try { const zones = await fetchZonesWithCache(serverId); setZones(zones); } catch (err) { // 这里处理错误,比如提示用户 console.error('加载时区失败:', err); } }; loadZones(); }, [serverId]);
2. 封装自定义Hook复用缓存逻辑
如果EventCard在多个地方使用,把缓存和状态管理封装成自定义Hook,让逻辑更清晰:
import { useState, useEffect } from 'react'; // 复用带缓存的fetch函数 const fetchZonesWithCache = (() => { const requestCache = new Map<string, Promise<Zone[]>>(); return async (serverId: string) => { if (requestCache.has(serverId)) { return requestCache.get(serverId)!; } const fetchPromise = fetch(`/api/zones?serverId=${serverId}`) .then(res => { if (!res.ok) throw new Error('请求失败'); return res.json(); }) .catch(err => { requestCache.delete(serverId); throw err; }); requestCache.set(serverId, fetchPromise); return fetchPromise; }; })(); // 自定义Hook function useZones(serverId: string) { const [zones, setZones] = useState<Zone[] | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<Error | null>(null); useEffect(() => { let isMounted = true; // 防止组件卸载后更新状态 const loadZones = async () => { try { const data = await fetchZonesWithCache(serverId); if (isMounted) { setZones(data); setLoading(false); } } catch (err) { if (isMounted) { setError(err as Error); setLoading(false); } } }; loadZones(); return () => { isMounted = false; }; }, [serverId]); return { zones, loading, error }; } // 在EventCard中使用 const { zones, loading, error } = useZones(serverId); // 渲染时可以根据loading/error状态处理 if (loading) return <div>加载中...</div>; if (error) return <div>加载失败:{error.message}</div>;
为什么useMemo/useCallback没解决问题?
useMemo和useCallback是用来缓存组件渲染结果或函数引用,避免不必要的重渲染,但它们无法控制API请求的重复发起——每个EventCard实例都是独立的,就算引用被缓存,每个实例还是会各自执行useEffect里的请求逻辑。核心解决方案是对请求本身做缓存,而不是缓存组件或函数。
内容的提问来源于stack exchange,提问作者dahool
相关产品推荐
相关产品推荐

