React Strict Mode 下 useEffect 为什么会执行两次?

Air-晨晰 阅读 2

我最近在开发一个 React 项目,启用了 Strict Mode 后发现 useEffect 里的逻辑执行了两次,比如发请求、打日志都重复了。明明只渲染了一次组件,为啥会这样?是不是我写法有问题?

我的代码结构大概是这样的:

import { useEffect, useState } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    console.log('Fetching data...');
    fetch('/api/data').then(res => res.json()).then(setData);
  }, []);

  return <div>{data ? data.name : 'Loading...'}</div>;
}

本地开发环境用的是 React 18,包裹了 <React.StrictMode>。去掉 Strict Mode 就正常了,但又怕以后上线出问题。这到底该怎么处理?

我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
司空馨冉
这是 React 18 的故意设计,不是你的写法有问题。

React 18 在开发模式的 Strict Mode 下会故意挂载组件两次、卸载一次、再挂载一次,用来检测你的副作用代码是否写得"干净"——也就是有没有做好清理工作。他们希望通过这种方式逼迫你写出能在组件卸载时正确清理的代码。

你那个 fetch 请求没有做取消处理,所以第二次挂载时前一个请求还在跑,就会出现重复请求的问题。

改一下就行,加 AbortController:

function MyComponent() {
const [data, setData] = useState(null);

useEffect(() => {
const controller = new AbortController();

fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});

return () => controller.abort();
}, []);

return
{data ? data.name : 'Loading...'}
;
}


这样即使 effect 被执行多次,之前的请求也会被取消,不会出现竞态问题。返回的那个函数就是 cleanup 函数,React 会在组件卸载或者下次 effect 执行前调用它。

线上生产环境不会有这个问题,Strict Mode 只在开发环境生效。但这个设计确实挺烦人的,不过长期来看是好事,能帮你发现潜在的内存泄漏和竞态条件。
点赞
2026-03-12 20:01