标准答案

  1. 请求竞态常见于搜索、筛选、分页、路由切换和重复提交,旧请求可能比新请求更晚返回。
  2. fetch 可以使用 AbortController 取消请求,axios 等库也有各自的取消方案。
  3. useEffect 中发起请求时,应在 cleanup 里 abort,或者用 requestId 判断返回结果是否仍然有效。
  4. 组件卸载后不应继续写入无意义状态,更不应展示已经离开页面的错误提示。
  5. 请求层负责取消能力、错误规范和超时,组件层负责判断当前业务是否还需要这次结果。

题目解析

这道题的核心是两类问题:旧请求覆盖新结果,以及组件已经卸载或条件已经变化后继续更新。

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 只清理事件监听,不处理请求、订阅和定时器。
  • 把取消请求当作错误提示给用户,造成误导。

作者信息