Next.js 18开发中useEffect重复执行的真相:React严格模式下的隐藏福利
最近在升级到Next.js 18的项目里,不少开发者都遇到了一个看似“诡异”的现象:明明在useEffect的依赖数组里写了空数组[],期望它只在组件挂载时执行一次,但在开发环境下,控制台里的日志却实实在在地打印了两次。第一反应往往是“我是不是写错了什么?”或者“这难道是Next.js 18的新Bug?”。如果你也为此困惑过,甚至尝试过搜索“如何关闭useEffect执行两次”,那么这篇文章正是为你准备的。但真相可能与你想象的恰恰相反——这非但不是Bug,反而是React团队精心设计、送给所有开发者的一个“调试大礼包”。理解它,能让你写出更健壮、更少内存泄漏的React应用。
这个现象的核心,源于React的严格模式(Strict Mode)。在Next.js 18中,默认启用了React 18的严格模式。这个模式在开发环境下,会故意让你的组件函数(包括useEffect)执行两次。它的目的不是为了给你添堵,而是为了模拟组件“挂载 -> 卸载 -> 再次挂载”的生命周期,强迫你提前面对和解决那些在真实用户操作中(比如快速切换标签页、路由导航)才会暴露的资源管理问题。对于追求代码质量、希望构建稳定企业级应用的中高级开发者而言,这无疑是一个将潜在风险扼杀在开发阶段的绝佳工具。接下来,我们就深入拆解这个“福利”背后的机制、影响以及如何正确地“享用”它。
1. 拨开迷雾:React严格模式的设计哲学
在急于寻找“解决方法”之前,我们有必要先理解React团队为何要引入这个看似“反直觉”的行为。React严格模式自推出以来,其核心目标始终是帮助开发者在开发阶段发现并修复潜在的副作用问题,从而提升应用的整体健壮性。
1.1 严格模式是什么?
简单来说,React严格模式是一个在开发环境下启用的工具。它通过一系列额外的检查和模拟行为,对你的组件代码进行“压力测试”。这些行为在生产构建中会被完全移除,因此不会影响线上用户的性能。在Next.js 18项目中,你可以在next.config.js中看到它的默认配置:
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true, // 默认即为true
}
module.exports = nextConfig
而在你的应用根组件外围,通常包裹着<React.StrictMode>标签(在Next.js App Router中,这由框架内部处理)。这个标签就是激活所有严格模式行为的开关。
注意:严格模式的行为仅作用于其包裹范围内的组件树。如果你的应用部分模块不需要此检查,可以将其移出
<React.StrictMode>的包裹范围,但这通常不推荐。
1.2 为何要模拟“双重渲染”?
React组件本质上是纯函数(至少在理想状态下)。给定相同的props和state,它们应该渲染出相同的结果。然而,副作用(如数据获取、订阅、手动DOM操作)的存在打破了这种纯粹性。严格模式通过故意让组件函数执行两次,来检测你的组件是否存在以下两类常见问题:
- 不纯的渲染逻辑:例如,在渲染函数中直接修改外部变量、调用
Date.now()或Math.random()等。两次执行如果产生不同结果,就能立刻被发现。 - 不完善的副作用清理:这是
useEffect双次执行重点关照的对象。它模拟了用户操作中组件被快速卸载又挂载的场景(比如用户点击一个按钮,又立刻点击返回),检查你是否在useEffect的清理函数中妥善释放了所有资源。
为了更直观地理解,我们看一个反面案例:
import { useEffect, useState } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null)

5597

被折叠的 条评论
为什么被折叠?



