竞态条件与 Effect 清理
在 useEffect 中使用 AbortController,在重新渲染时取消过期的 fetch 请求。
竞态条件与 Effect 清理 是 CoddyKit 上的免费 React Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Academy 课程共包含 4 节课。
欢迎
在本课中,您将学习在获取数据的 React 副作用中如何发生竞态条件,以及如何使用 AbortController 进行清理来消除这些问题。
什么是竞态条件
当两个异步操作以不可预测的顺序完成时,就会发生竞态条件。在 React 中,如果用户快速在不同项目之间导航,项目 A 的缓慢获取请求可能会在项目 B 的请求之后才完成,从而显示过时的数据。
问题:过时的获取响应
如果没有清理,并且组件在旧的获取请求完成前因新的 ID 重新渲染,那么两个请求都会运行。较旧且较慢的请求可能会覆盖状态中的较新数据。
useEffect(() => {
fetch('/api/item/' + id)
.then(r => r.json())
.then(data => setData(data)); // may run AFTER a newer fetch!
}, [id]);修复方法 1:忽略标志
一种简单的修复方法是在副作用中设置布尔值 `ignore` 标志。清理函数会将其设为真值。获取回调在调用 setState 前检查该标志。
useEffect(() => {
let ignore = false;
fetch('/api/item/' + id)
.then(r => r.json())
.then(data => { if (!ignore) setData(data); });
return () => { ignore = true; };
}, [id]);修复方法 2:AbortController
AbortController 是现代标准。在副作用中创建一个控制器,将其信号传给获取请求,并在清理函数中中止请求。获取请求会抛出 AbortError,您可以忽略它。
useEffect(() => {
const controller = new AbortController();
fetch('/api/item/' + id, { signal: controller.signal })
.then(r => r.json())
.then(data => setData(data))
.catch(err => {
if (err.name === 'AbortError') return; // expected
setError(err.message);
});
return () => controller.abort();
}, [id]);使用 AbortController 与异步/等待
在 useEffect 中使用异步函数和 try/catch。请在 catch 代码块中忽略 AbortError。
useEffect(() => {
const ctrl = new AbortController();
async function load() {
try {
const res = await fetch('/api/' + id, { signal: ctrl.signal });
const json = await res.json();
setData(json);
} catch (e) {
if (e.name !== 'AbortError') setError(e.message);
}
}
load();
return () => ctrl.abort();
}, [id]);始终需要清理
即使竞态条件不太可能发生(例如挂载时只获取一次数据),也请始终添加清理。在 React 18 StrictMode 中,副作用会执行两次——如果没有清理,第一个被放弃的请求就会污染状态。
useEffect 的异步陷阱
useEffect 的回调不能是 `async`。请在副作用中定义一个异步函数并立即调用它,或者使用带有 .then()/.catch() 的普通函数。
useEffect(() => {
// useEffect callback is NOT async
async function fetchData() { /* ... */ }
fetchData(); // call it
return () => ctrl.abort();
}, [id]);库会为您处理这些问题
React Query、SWR 和 RTK Query 都会自动处理竞态条件、中止和清理。使用这些库时,您不需要手动编写 AbortController 代码。
测试竞态条件
要手动测试竞态条件:打开网络 DevTools,将网络限制为“慢速 3G”,然后快速点击不同项目。没有清理时,您会看到过时数据一闪而过。使用 AbortController 后,只会显示最后选中的项目。
快速检查
在 useEffect 的清理函数中调用 controller.abort() 会做什么?
回顾
当过时的获取响应在较新的请求之后更新状态时,就会发生竞态条件。请使用忽略标志或 AbortController 进行修复。在 useEffect 的清理中始终中止或忽略请求。React Query 等库会自动处理这些问题。
下一课
下一课:**使用 useTransition 实现非阻塞界面更新**——您将把开销较大的状态更新包裹在 startTransition 中,以保持界面响应迅速。
常见问题解答
「竞态条件与 Effect 清理」课时是免费的吗?
是的 — 「竞态条件与 Effect 清理」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Academy 课程共包含 4 节课。
「竞态条件与 Effect 清理」这节课中我会学到什么?
在 useEffect 中使用 AbortController,在重新渲染时取消过期的 fetch 请求。 你通过在浏览器中直接运行的动手代码来练习 React Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「竞态条件与 Effect 清理」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Academy 课中编写并运行代码吗?
能。每节 React Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。