React18 严格模式下的双重渲染之谜

文章目录

问题

这是一个 React Demo 项目 ,启动后在 Chrome 控制台里发现 控制台 被打印了 "两次",但是,在生产环境中,这个问题通常不会出现,所以可以主要关注开发环境中的行为。

javascript 复制代码
// 入口文件
import { StrictMode } from 'react';
import * as ReactDOMClient from 'react-dom/client';
import App from './App';
const root = ReactDOMClient.createRoot(document.getElementById('root'));
root.render(
  <StrictMode>
    <App />
  </StrictMode>
);
javascript 复制代码
// 组件代码
import React, { useEffect } from 'react';

const App = () => {
  useEffect(() => {
    console.log('组件挂载完成!');
  }, []);
  return <>Hello world!</>;
};

分析

  1. 这是 React18 才新增的特性。

  2. 仅在开发模式("development")下,且使用了严格模式("Strict Mode")下会触发。

    生产环境("production")模式下和原来一样,仅执行一次。

  3. 之所以执行两次,是为了模拟立即卸载组件和重新挂载组件。

    为了帮助开发者提前发现重复挂载造成的 Bug 的代码。

    同时,也是为了以后 React的新功能做铺垫。

    未来会给 React 增加一个特性,允许 React 在保留状态的同时,能够做到仅仅对UI部分的添加和删除。

    让开发者能够提前习惯和适应,做到组件的卸载和重新挂载之后, 重复执行 useEffect的时候不会影响应用正常运行。

  4. 每次组件渲染时,React 都会更新页面 UI,然后运行 useEffect 中的代码

  5. Effect 在屏幕更新之后的 rendering 进程结束的时候执行。

解决

方式一:使用状态标志

javascript 复制代码
const [isDataFetched, setIsDataFetched] = useState(false);

useEffect(() => {
  if (!isDataFetched) {
    getDataList();
    setIsDataFetched(true);
  }
}, [isDataFetched]);

方式二:使用 useRef

javascript 复制代码
const dataFetchedRef = useRef(false);

useEffect(() => {
  if (dataFetchedRef.current) return;
  dataFetchedRef.current = true;
  getDataList();
}, []);

方式三:移除严格模式

方式四:使用 useCallback

将 getDataList 函数用 useCallback 包裹,可以确保它的引用稳定性。

相关推荐
To_OC21 小时前
LC 560 和为 K 的子数组:前缀和配哈希表,这对组合我是真的服了
javascript·算法·程序员
明朝百晓生1 天前
Deep RL learning[2026/8]
开发语言·javascript·人工智能
用户938515635071 天前
ESLint 代码规范完全指南——从 AST 原理到 flat config 逐行解析
javascript·后端·代码规范
kyriewen1 天前
今年裁了16万技术人——但字节前端岗反而涨了23%
前端·javascript·面试
王林不想说话1 天前
ES6 到 ES2026 全面进阶指南:新特性、原理、示例与工程落地
前端·javascript·面试
WZzz1 天前
JavaScript 中 this 的「指向」完整教程
javascript
mONESY1 天前
浏览器里跑 DeepSeek-R1,这份 worker.js 到底写了什么
javascript
用户6919026813391 天前
用浏览器 WebGPU跑DPSK大模型(1) - 模型的下载和前端下载进度的显示
javascript·react.js·架构
夏幻灵1 天前
Vue 2 与 Vue 3 在应用初始化上的设计差异与技术演进
前端·javascript·vue.js
悟空瞎说1 天前
Three.js 架构实践:用 MVC 模式组织你的 3D 应用
前端·javascript