React如何检测courses、packages、programs数据全部加载完成
现有代码问题
- 初始进入页面、切换筛选条件时没有主动将
isLoading设为true,加载态触发逻辑缺失 - 每个接口请求的回调中单独调用
setIsLoading(false),会导致最先返回的请求直接关闭加载态,其余还在请求中的数据未完成加载就提前展示内容,不符合「所有数据加载完成才隐藏loading」的需求 - 缺少竞态处理,快速切换筛选条件时,旧请求晚返回会覆盖新数据,还会错误关闭当前加载状态
修复后的实现
核心逻辑是统一收集当前筛选条件下需要发起的所有请求,等待全部请求完成后再关闭加载态,同时增加竞态标记避免状态错乱:
const [isLoading, setIsLoading] = React.useState<boolean>(false); React.useEffect(() => { // 标记当前effect是否已过期,避免切换筛选后旧请求返回覆盖状态 let isIgnore = false; // 重置状态,开启加载态 setIsLoading(true); setCourses([]); setPrograms([]); setPackages([]); // 收集本次筛选条件下需要发起的所有请求 const requestQueue: Promise<any>[] = []; if ([DataType.Course, "all"].includes(contentType)) { requestQueue.push( Course.get(undefined, useCatalog, parent).then((c: Course[]) => { if (!isIgnore) setCourses(c || []); }) ); } if ([DataType.Program, "all"].includes(contentType) && (!parent || parent.data_type === DataType.Package)) { requestQueue.push( Program.get(undefined, useCatalog, parent).then((p: Program[]) => { if (!isIgnore) setPrograms(p || []); }) ); } if ([DataType.Package, "all"].includes(contentType) && !parent) { requestQueue.push( Package.get(undefined, useCatalog).then((p: Package[]) => { if (!isIgnore) setPackages(p || []); }) ); } // 所有请求全部完成后再关闭加载态 Promise.all(requestQueue).finally(() => { if (!isIgnore) setIsLoading(false); }); // 依赖项变化触发下一次effect前,标记当前effect过期 return () => { isIgnore = true; }; }, [contentType, useCatalog, parent]);
逻辑说明
- 每次筛选条件、依赖项变化触发effect时,第一时间重置旧数据、开启加载态
- 根据当前筛选规则判断需要加载的数据类型,把对应接口请求统一加入请求队列,不会漏判待加载项
- 借助
Promise.all等待队列中所有请求完成(无论请求成功或失败都会触发finally),再关闭加载态,保证所有待加载数据全部返回后才隐藏加载旋转器 - 过期标记可以避免快速切换筛选时,旧请求返回干扰新的加载流程、覆盖新数据
- 无待加载请求时,空队列的
Promise.all会直接执行完成逻辑,不会出现加载态常驻的问题
内容的提问来源于stack exchange,提问作者agobago
相关产品推荐
相关产品推荐

