创见博客
React严格模式(Strict Mode)
七崽爱吃小饼干2026/02/27阅读 1专栏 React

一、React 严格模式是什么?

React 严格模式(React.StrictMode)是一个开发环境专用的工具组件,它不会渲染任何可见的 UI,主要作用是帮你在开发过程中提前发现代码里的潜在问题、检测不安全的生命周期、识别过时的 API 使用等,从而写出更健壮、更符合 React 最佳实践的代码。

核心特点:

  • 仅在 development(开发)环境生效,production(生产)环境会自动禁用,不会带来任何性能开销。
  • 它是一个“检测工具”,而非“限制工具”,只会给出警告,不会阻止代码运行。
  • 可以嵌套使用,作用于其包裹的所有子组件(包括深层子组件)。

二、如何使用严格模式?

使用方式非常简单,只需用 <StrictMode> 包裹你想要检测的组件树即可,通常会包裹根组件(如 App 或整个应用)。

1. React 18 及以上(基于 createRoot)

jsx
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. 局部启用(仅检测特定组件)

也可以只包裹部分组件,只检测该部分的代码:

jsx
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 等纯函数

示例:为什么会重复执行?

jsx
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> 包裹即可:

jsx
// 禁用严格模式
root.render(<App />);

五、为什么严格模式能检测这类清理问题?

核心逻辑是:在开发环境下,严格模式会模拟组件的“挂载-卸载-重新挂载”过程,以此触发副作用的清理函数(cleanup),让你直观看到是否存在清理不彻底的问题。

具体行为:

  1. 组件首次挂载时,执行 useEffect/useLayoutEffect 中的逻辑(如发起 AJAX、创建定时器)。
  2. 严格模式会立即触发模拟卸载,调用清理函数(如果你写了的话)。
  3. 紧接着又模拟重新挂载,再次执行 useEffect 中的逻辑。
  4. 这个过程仅发生在开发环境,目的就是暴露“清理函数缺失/错误”的问题。

六、实战演示:检测定时器是否正确清除

反面例子(未正确清除定时器):

jsx
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),直接暴露“定时器未清理”的问题。

正面例子(正确清除定时器):

jsx
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 请求):

jsx
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 请求):

jsx
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>;
}

总结

  1. React 严格模式是开发环境专用的检测工具,生产环境无任何影响,核心目的是提前发现代码潜在问题。
  2. 最核心的行为是重复调用纯函数/副作用相关函数,用于检测不可预期的副作用,这是新手最容易困惑但也是最有价值的特性。
  3. 严格模式不会渲染任何 UI,只需包裹组件树即可生效,建议开发阶段始终启用,上线前再通过它修复所有警告。
  4. React 严格模式通过模拟组件挂载-卸载-重新挂载的过程,能直接暴露定时器、AJAX 请求等副作用未被正确清理的问题。
  5. 检测的核心是触发 useEffect 的清理函数,验证你是否写了完善的清理逻辑(清除定时器、取消请求)。
  6. 利用严格模式的这个特性,能提前发现生产环境中可能出现的内存泄漏、无效请求、setState 警告等隐性问题。

通过严格模式的检测,你可以确保代码符合 React 的最佳实践,避免在生产环境中出现难以排查的隐性问题。

评论
0/100