react-useEffect数据请求时的竞态条件

loading 2023年12月29日 60次浏览

许多应用使用 Effect 来发起数据获取请求。像这样在 Effect 中写一个数据获取请求是相当常见的:

function SearchResults({ query }) {
  useEffect(() => {
    // 🔴 避免:没有清除逻辑的获取数据
    fetchResults(query, page).then(json => {
      setResults(json);
    });
  }, [query, page]);
}

然而,上面的代码有一个问题。假设快速地输入 “hello”。那么 query 会从 “h” 变成 “he”,“hel”,“hell” 最后是 “hello”。这会触发一连串不同的数据获取请求,但无法保证对应的返回顺序。例如,“hell” 的响应可能在 “hello” 的响应之后返回。由于它的 setResults() 是在最后被调用的,你将会显示错误的搜索结果。

这种情况被称为 “竞态条件”:两个不同的请求 “相互竞争”,并以与你预期不符的顺序返回。

为了修复这个问题,需要添加一个清理函数来忽略较早的返回结果:

useEffect(() => {
    let ignore = false;

    fetchResults(query, page).then(json => {
      if (!ignore) {
        setResults(json);
      }
    });

    return () => {
      ignore = true;
    };
  }, [query, page]);

这确保了在 Effect 中获取数据时,除了最后一次请求的所有返回结果都将被忽略。