标准答案
- 请求竞态常见于搜索、筛选、分页、路由切换和重复提交,旧请求可能比新请求更晚返回。
- fetch 可以使用 AbortController 取消请求,axios 等库也有各自的取消方案。
- useEffect 中发起请求时,应在 cleanup 里 abort,或者用 requestId 判断返回结果是否仍然有效。
- 组件卸载后不应继续写入无意义状态,更不应展示已经离开页面的错误提示。
- 请求层负责取消能力、错误规范和超时,组件层负责判断当前业务是否还需要这次结果。
题目解析
这道题的核心是两类问题:旧请求覆盖新结果,以及组件已经卸载或条件已经变化后继续更新。
AbortController 适合真正取消网络请求;requestId 或 ignore 标记适合忽略旧结果。
React 18 移除了部分卸载后 setState 的警告,但这不代表可以忽略清理。无意义请求和错误回写仍然会影响体验。
代码示例
下面用 AbortController 在依赖变化或组件卸载时取消旧请求。
TSX
function SearchResult({ keyword }: { keyword: string }) {
const [items, setItems] = useState<Item[]>([])
useEffect(() => {
const controller = new AbortController()
fetchItems(keyword, { signal: controller.signal })
.then(setItems)
.catch(error => {
if (error.name !== 'AbortError') {
throw error
}
})
return () => controller.abort()
}, [keyword])
return <ResultList items={items} />
}常见误区
- 快速切换条件时没有取消或忽略旧请求,导致过期数据覆盖新结果。
- cleanup 只清理事件监听,不处理请求、订阅和定时器。
- 把取消请求当作错误提示给用户,造成误导。