You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React 18组件mount-unmount-remount异常:useEffect执行两次问题求助

React 18中useEffect在mount阶段执行两次的解决方案

问题原因

这是React 18在开发环境下Strict Mode的新增行为:为了帮开发者提前排查未正确清理的副作用(比如未取消的订阅、未终止的请求等),Strict Mode会模拟组件「挂载→卸载→重新挂载」的过程,导致空依赖的useEffect执行两次。生产环境下不会触发该行为,useEffect仍只会执行一次。

解决方案

方案1:临时关闭Strict Mode(不推荐长期使用)

如果只是开发阶段想快速还原React 17的行为,可修改项目入口文件(通常是index.js或main.jsx),移除<StrictMode>的包裹:

// React 18默认入口代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// 修改后代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

方案2:让副作用支持重复执行(推荐)

正确的做法是适配React 18的并发特性,确保副作用代码能被正确清理,即使执行多次也不会产生异常。以API请求场景为例,使用AbortController取消未完成的请求:

import { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [apiCalled, setApiCalled] = useState(0);

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

    const callApi = async () => {
      try {
        // 模拟API请求逻辑
        await new Promise(resolve => setTimeout(resolve, 100));
        // 仅在请求未被取消时更新状态
        if (!signal.aborted) {
          setApiCalled(prev => prev + 1);
        }
      } catch (error) {
        // 忽略取消请求的错误
        if (error.name !== 'AbortError') {
          console.error('API请求失败:', error);
        }
      }
    };

    callApi();
    // 组件卸载时终止请求
    return () => controller.abort();
  }, []);

  return <div className="App">Api Called: {apiCalled}</div>;
}

这样即使Strict Mode触发两次挂载,第一次的请求会被主动终止,最终只会更新一次状态,符合预期。


内容的提问来源于stack exchange,提问作者Yatin Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 01:20:20