React搜索组件useEffect异步任务并发问题:如何取消旧请求?
解决React useEffect异步任务竞态问题:终止旧搜索任务
当搜索条件频繁变更时,旧的异步加载/过滤任务未及时终止,会与新任务并行执行并覆盖组件状态,导致最终结果异常。可通过以下方式解决:
核心思路
利用useEffect的清理函数设置取消标志,在新任务触发时标记旧任务为已取消,让旧任务在关键执行节点检查该标志,一旦确认取消就终止流程,不再更新组件状态。
修改后的完整代码
React.useEffect(() => { let isCancelled = false; // 取消标志 const loadAndFilterPosts = async () => { let postsToLoad = 300; let allLoadedPosts: any = []; let filteredPosts = []; let keys: string[] = []; let i = 0; // 获取分类Redis keys后检查是否已取消 if (chosenCategory && chosenCategory.id) { keys = await getKeys(chosenCategory.id); if (isCancelled) return; // 取消则终止流程 } // 循环条件加入取消检查,避免已取消后继续执行 while (allLoadedPosts.length < postsToLoad && !isCancelled) { let newPosts = await loadPostsFromRedis(i, keys); if (isCancelled) return; // 异步加载后检查取消状态 if (!newPosts.length) { break; } // 免费期过滤逻辑 if (infoUser && infoUser?.categoriesFreePeriod?.length > 0 && !infoUser?.endFreePeriod) { const currentDate = new Date(); currentDate.setDate(currentDate.getDate() - 3); newPosts = newPosts.filter((item: any) => new Date(item.post_date_publish*1000).getTime() < currentDate.getTime() ); } allLoadedPosts = [...allLoadedPosts, ...newPosts]; filteredPosts = applyFilters(allLoadedPosts); // 更新状态前检查取消标志,防止旧任务覆盖新状态 if (!isCancelled) { setFilteredPosts(filteredPosts.sort((a: any, b: any) => a.data - b.data)); } if (filteredPosts.length >= postsToLoad) { break; } postsToLoad += 300; i++; } }; loadAndFilterPosts(); setPage(1); // 清理函数:依赖项变更或组件卸载时标记旧任务为取消 return () => { isCancelled = true; }; }, [keyWords, keyCityWords, social, postsCount, chosenCategory])
关键修改说明
- 新增
isCancelled变量作为取消状态标识 - 在每一次异步操作(
await)结束后立即检查取消标志,若已取消则直接终止函数 - 循环条件中加入
!isCancelled,避免已取消的任务继续执行循环逻辑 - 在调用
setFilteredPosts更新状态前检查标志,防止旧任务覆盖新任务的状态结果 - 通过useEffect的清理函数,在依赖项变更或组件卸载时自动标记旧任务为取消状态
内容的提问来源于stack exchange,提问作者Максим Дмитриевич
相关产品推荐
相关产品推荐

