多请求写入数组状态变量导致状态不一致问题排查
解决React异步请求状态数组无法正确累积的问题
你的问题出在闭包捕获旧状态上:每个异步请求的finally块里,flashingBoxLoaderItems是请求发起时捕获的旧状态快照。当多个请求几乎同时完成时,每个setFlashingBoxLoaderItems([...flashingBoxLoaderItems, 'fetchN'])都是基于同一个旧数组更新,导致后面的更新会覆盖前面的,最终数组里只保留最后几个请求的凭证。
修复方案:使用函数式更新
React的setState支持传入函数,函数会接收最新的状态值作为参数,这样就能确保每次更新都是基于最新的数组:
修改每个请求的finally部分:
const fetch1 = async() => { try { const response = await get<any>("/url1/"); //... } catch { //... } finally { // 用函数式更新获取最新状态 setFlashingBoxLoaderItems(prevItems => [...prevItems, 'fetch1']); } }
同理修改fetch2、fetch3等所有请求的setState逻辑,这样不管请求完成顺序如何,数组都会正确累积所有凭证。
额外优化:避免重复添加(可选)
如果担心某个请求可能重复执行(比如误触发多次),可以在更新时检查是否已存在该凭证,避免数组里出现重复项:
setFlashingBoxLoaderItems(prevItems => { if (!prevItems.includes('fetch1')) { return [...prevItems, 'fetch1']; } return prevItems; // 已存在则不修改 });
或者直接使用Set类型存储状态,天然去重:
// 初始化状态为Set const [flashingBoxLoaderItems, setFlashingBoxLoaderItems] = useState<Set<string>>(new Set()); // 请求的finally里更新Set setFlashingBoxLoaderItems(prevSet => new Set(prevSet).add('fetch1')); // 校验所有请求完成的逻辑 useEffect(() => { const fetchesExpected = ['fetch1','fetch2','fetch3','fetch4','fetch5']; const result = fetchesExpected.every(i => flashingBoxLoaderItems.has(i)); setFlashingBoxLoaderComplete(result); }, [flashingBoxLoaderItems]);
简化状态:去掉单独的complete变量(可选)
其实flashingBoxLoaderComplete不需要单独作为状态存储,可以用useMemo直接计算得出,减少不必要的状态更新:
const fetchesExpected = ['fetch1','fetch2','fetch3','fetch4','fetch5']; const flashingBoxLoaderComplete = useMemo(() => { return fetchesExpected.every(i => flashingBoxLoaderItems.includes(i)); }, [flashingBoxLoaderItems]);
这样就可以删掉原来的flashingBoxLoaderComplete状态和对应的useEffect,代码更简洁。
内容的提问来源于stack exchange,提问作者gene b.
相关产品推荐
相关产品推荐

