React 词条目录
前端Wiki/React/进阶/useEffect 副作用

清理函数与请求竞态

effect 可以返回一个清理函数:组件卸载时、或下一次 effect 执行前会被调用。定时器要 clearTimeout、订阅要 removeEventListener、 WebSocket 要 close——有“开启”就必须有“清理”,否则会内存泄漏与重复执行。

典型场景:组件卸载后请求才返回 → setState 作用在已卸载组件上(React 18 已不再警告,但仍浪费)。正确做法是用清理函数配合 AbortController 取消未完成的请求,顺便解决“快速切换时旧请求覆盖新结果”的竞态问题。

  • return 的清理函数在下一次 effect 执行前、组件卸载时调用
  • 有开启就有清理:定时器、订阅、监听、请求
  • AbortController 取消请求:防卸载后更新 + 防竞态
HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>清理函数</title>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect } = React;

function Timer() {
  const [sec, setSec] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setSec(s => s + 1), 1000);
    // 清理函数:组件卸载时停掉定时器,否则一直空跑
    return () => clearInterval(id);
  }, []);
  return <p>已显示 {sec} 秒</p>;
}

function App() {
  const [show, setShow] = useState(true);
  const [q, setQ] = useState('React');

  // 用 AbortController 取消旧请求:防竞态 + 防卸载后更新
  useEffect(() => {
    const ctrl = new AbortController();
    fetch('https://httpbin.org/get?q=' + q, { signal: ctrl.signal })
      .then(r => r.json())
      .then(() => console.log('查询完成:', q))
      .catch(e => { if (e.name !== 'AbortError') console.error(e); });
    return () => ctrl.abort();   // q 变化或卸载时取消上一个
  }, [q]);

  return (
    <div style={{ padding: 16 }}>
      <button onClick={() => setShow(s => !s)}>{show ? '卸载' : '挂载'}计时器</button>
      {show && <Timer />}
      <hr />
      <input value={q} onChange={e => setQ(e.target.value)} />
      <p>快速输入时,只会保留最后一次请求的结果</p>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
本节要点有“开”必有“清”:return 清理函数在卸载/重跑前调用;请求用 AbortController 取消防竞态。
🔗 相关词条PromiseJavaScriptNode.js