一、React 严格模式是什么?
React 严格模式(React.StrictMode)是一个开发环境专用的工具组件,它不会渲染任何可见的 UI,主要作用是帮你在开发过程中提前发现代码里的潜在问题、检测不安全的生命周期、识别过时的 API 使用等,从而写出更健壮、更符合 React 最佳实践的代码。
核心特点:
- 仅在
development(开发)环境生效,production(生产)环境会自动禁用,不会带来任何性能开销。 - 它是一个“检测工具”,而非“限制工具”,只会给出警告,不会阻止代码运行。
- 可以嵌套使用,作用于其包裹的所有子组件(包括深层子组件)。
二、如何使用严格模式?
使用方式非常简单,只需用 <StrictMode> 包裹你想要检测的组件树即可,通常会包裹根组件(如 App 或整个应用)。
1. React 18 及以上(基于 createRoot)
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>
);
2. 局部启用(仅检测特定组件)
也可以只包裹部分组件,只检测该部分的代码:
import React from 'react';
function Home() {
return (
<div>
{/* 仅检测 UserCard 组件及其子组件 */}
<React.StrictMode>
<UserCard />
</React.StrictMode>
<OtherComponent /> {/* 不受严格模式检测 */}
</div>
);
}
三、严格模式的核心检测能力
严格模式的核心价值在于它会触发一系列“额外检查”,下面是最常用、最核心的检测项:
1. 检测不安全的生命周期
React 会警告你使用了已废弃/不安全的生命周期方法,比如:
componentWillMount(已废弃)componentWillReceiveProps(已废弃)componentWillUpdate(已废弃)
这些生命周期在 React 并发模式下可能导致不可预测的行为,严格模式会在控制台明确提示你替换为新的 API(如 getDerivedStateFromProps、useEffect 等)。
2. 检测过时的 ref 用法
比如使用 React.createRef() 之外的方式(如直接给 DOM 元素赋值 this.refs.xxx),严格模式会警告你使用标准的 ref 用法。
3. 检测意外的副作用(最易被误解的点)
这是严格模式最核心也最容易让新手困惑的行为:在开发环境下,严格模式会故意重复调用某些函数,目的是让你发现代码中“不可预期的副作用”。
被重复调用的函数包括:
- 组件的函数体(函数组件本身)
useState/useReducer的初始化函数useEffect的清理函数(cleanup)- 类组件的
constructor、render、shouldComponentUpdate等纯函数
示例:为什么会重复执行?
import React, { useEffect, useState } from 'react';
function TestComponent() {
const [count, setCount] = useState(0);
// 函数组件体:开发环境下会被严格模式调用 2 次
console.log('组件渲染了');
useEffect(() => {
console.log('useEffect 执行');
// 清理函数:也会被调用 2 次
return () => {
console.log('useEffect 清理');
};
}, []);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
export default function App() {
return (
<React.StrictMode>
<TestComponent />
</React.StrictMode>
);
}
运行结果(开发环境):
组件渲染了
组件渲染了
useEffect 执行
useEffect 清理 // 严格模式触发的额外清理
useEffect 执行
关键提醒:
- 这种重复调用仅发生在开发环境,生产环境不会出现。
- 设计目的:让你意识到“组件函数/纯函数应该是无副作用的”——如果你的代码因为重复执行而出错,说明代码写得不够健壮(比如在组件体里直接修改全局变量、发起网络请求等)。
- 正确做法:副作用必须放在
useEffect/useLayoutEffect中,且清理函数要能正确处理重复调用。
4. 检测上下文(Context)的滥用
比如在上下文提供者中使用不稳定的值(如每次渲染都创建新对象),严格模式会警告你优化上下文依赖。
5. 检测遗留的 API(如 findDOMNode)
ReactDOM.findDOMNode() 是一个过时的 API,严格模式会警告你替换为 ref 直接引用 DOM 元素。
四、如何临时禁用严格模式?
如果开发中需要临时跳过严格模式的检测(比如调试重复执行的问题),只需移除 <StrictMode> 包裹即可:
// 禁用严格模式
root.render(<App />);
五、为什么严格模式能检测这类清理问题?
核心逻辑是:在开发环境下,严格模式会模拟组件的“挂载-卸载-重新挂载”过程,以此触发副作用的清理函数(cleanup),让你直观看到是否存在清理不彻底的问题。
具体行为:
- 组件首次挂载时,执行
useEffect/useLayoutEffect中的逻辑(如发起 AJAX、创建定时器)。 - 严格模式会立即触发模拟卸载,调用清理函数(如果你写了的话)。
- 紧接着又模拟重新挂载,再次执行
useEffect中的逻辑。 - 这个过程仅发生在开发环境,目的就是暴露“清理函数缺失/错误”的问题。
六、实战演示:检测定时器是否正确清除
反面例子(未正确清除定时器):
import React, { useEffect, useState } from 'react';
function TimerComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// 创建定时器,但未写清理函数
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
// ❌ 缺失清理函数:严格模式会让你发现问题
}, []);
return <div>Count: {count}</div>;
}
export default function App() {
return (
<React.StrictMode>
<TimerComponent />
</React.StrictMode>
);
}
问题表现(开发环境):
- 严格模式触发“挂载-卸载-重新挂载”,导致创建了两个定时器(原本只该有一个)。
- 页面上的
count会以 2 倍速度增长(1秒+2),直接暴露“定时器未清理”的问题。
正面例子(正确清除定时器):
import React, { useEffect, useState } from 'react';
function TimerComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
// ✅ 写清理函数:清除定时器
return () => {
clearInterval(timer);
};
}, []);
return <div>Count: {count}</div>;
}
export default function App() {
return (
<React.StrictMode>
<TimerComponent />
</React.StrictMode>
);
}
效果(开发环境):
- 首次挂载:创建定时器 → 严格模式触发卸载,执行清理函数(清除定时器)→ 重新挂载,创建新的定时器。
- 最终页面上
count正常每秒+1,说明清理逻辑生效,没有残留定时器。
七、实战演示:检测 AJAX 请求是否正确取消
AJAX 请求的核心是“取消未完成的请求”(比如组件卸载时,终止还在pending的请求),避免无用的网络请求占用资源,或导致组件卸载后 setState 报错。
以 Axios 为例(Fetch API 可通过 AbortController 实现):
反面例子(未取消 AJAX 请求):
import React, { useEffect, useState } from 'react';
import axios from 'axios';
function DataComponent() {
const [data, setData] = useState(null);
useEffect(() => {
// 发起请求,但未处理取消逻辑
axios.get('https://jsonplaceholder.typicode.com/todos/1')
.then(res => setData(res.data))
.catch(err => console.log(err));
// ❌ 组件卸载时,请求仍会继续,可能导致 setState 警告
}, []);
return <div>{data ? data.title : 'Loading...'}</div>;
}
正面例子(正确取消 AJAX 请求):
import React, { useEffect, useState } from 'react';
import axios from 'axios';
function DataComponent() {
const [data, setData] = useState(null);
useEffect(() => {
// 创建取消令牌
const source = axios.CancelToken.source();
// 发起请求并关联取消令牌
axios.get('https://jsonplaceholder.typicode.com/todos/1', {
cancelToken: source.token
})
.then(res => setData(res.data))
.catch(err => {
// 捕获取消请求的错误,避免控制台报错
if (axios.isCancel(err)) {
console.log('请求已取消:', err.message);
} else {
console.log('请求失败:', err);
}
});
// ✅ 清理函数:取消请求
return () => {
source.cancel('组件卸载,取消请求');
};
}, []);
return <div>{data ? data.title : 'Loading...'}</div>;
}
总结
- React 严格模式是开发环境专用的检测工具,生产环境无任何影响,核心目的是提前发现代码潜在问题。
- 最核心的行为是重复调用纯函数/副作用相关函数,用于检测不可预期的副作用,这是新手最容易困惑但也是最有价值的特性。
- 严格模式不会渲染任何 UI,只需包裹组件树即可生效,建议开发阶段始终启用,上线前再通过它修复所有警告。
- React 严格模式通过模拟组件挂载-卸载-重新挂载的过程,能直接暴露定时器、AJAX 请求等副作用未被正确清理的问题。
- 检测的核心是触发
useEffect的清理函数,验证你是否写了完善的清理逻辑(清除定时器、取消请求)。 - 利用严格模式的这个特性,能提前发现生产环境中可能出现的内存泄漏、无效请求、setState 警告等隐性问题。
通过严格模式的检测,你可以确保代码符合 React 的最佳实践,避免在生产环境中出现难以排查的隐性问题。