<StrictMode>

<StrictMode> 让你在开发早期就能找到组件中的常见错误。

<StrictMode>
<App />
</StrictMode>

参考

<StrictMode>

使用 StrictMode 为内部组件树启用额外的开发行为和警告

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

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

请参阅下面的更多示例。

严格模式启用以下仅限开发的行为

属性

StrictMode 不接受任何属性。

注意事项

  • <StrictMode> 包装的树中,无法选择退出严格模式。这让你确信 <StrictMode> 内部的所有组件都经过了检查。如果在产品上工作的两个团队对这些检查是否有价值存在分歧,他们需要达成共识,或者将 <StrictMode> 移动到树的更下层。

用法

为整个应用程序启用严格模式

严格模式会在 <StrictMode> 组件内的整个组件树上启用额外的仅限开发环境的检查。这些检查可以帮助你在开发过程的早期发现组件中的常见错误。

要为整个应用程序启用严格模式,请在渲染根组件时使用 <StrictMode> 包裹它

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

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

我们建议在严格模式下包裹你的整个应用程序,特别是对于新创建的应用程序。如果你使用的是为你调用 createRoot 的框架,请查看其文档以了解如何启用严格模式。

尽管严格模式检查**仅在开发环境中运行,**但它们可以帮助你发现代码中已经存在的错误,而这些错误在生产环境中可能难以可靠地重现。严格模式允许你在用户报告错误之前修复它们。

注意

严格模式在开发环境中启用以下检查

所有这些检查都只在开发环境中进行,不会影响生产构建。


为应用程序的一部分启用严格模式

你也可以为应用程序的任何部分启用严格模式

import { StrictMode } from 'react';

function App() {
return (
<>
<Header />
<StrictMode>
<main>
<Sidebar />
<Content />
</main>
</StrictMode>
<Footer />
</>
);
}

在这个例子中,严格模式检查不会对 HeaderFooter 组件运行。但是,它们会在 SidebarContent 上运行,以及它们内部的所有组件,无论嵌套多深。


修复在开发中通过双重渲染发现的错误

React 假设你编写的每个组件都是一个纯函数。这意味着你编写的 React 组件必须在给定相同的输入(属性、状态和上下文)时始终返回相同的 JSX。

违反此规则的组件会表现得不稳定并导致错误。为了帮助你发现意外的不纯代码,严格模式会在开发环境中**调用某些函数(仅限那些应该是纯函数的函数)两次。**这包括

如果一个函数是纯函数,则运行两次不会改变其行为,因为纯函数每次都会产生相同的结果。但是,如果一个函数是不纯的(例如,它会改变它接收的数据),则运行两次往往会很明显(这就是它不纯的原因!)这可以帮助你在早期发现并修复错误。

下面是一个例子,说明严格模式下的双重渲染如何帮助你尽早发现错误。

StoryTray 组件接受一个 stories 数组,并在末尾添加一个“创建故事”项

export default function StoryTray({ stories }) {
  const items = stories;
  items.push({ id: 'create', label: 'Create Story' });
  return (
    <ul>
      {items.map(story => (
        <li key={story.id}>
          {story.label}
        </li>
      ))}
    </ul>
  );
}

上面的代码中有一个错误。但是,它很容易被忽略,因为初始输出看起来是正确的。

如果 StoryTray 组件多次重新渲染,则此错误将变得更加明显。例如,让我们在将鼠标悬停在 StoryTray 上时,使用不同的背景颜色重新渲染它

import { useState } from 'react';

export default function StoryTray({ stories }) {
  const [isHover, setIsHover] = useState(false);
  const items = stories;
  items.push({ id: 'create', label: 'Create Story' });
  return (
    <ul
      onPointerEnter={() => setIsHover(true)}
      onPointerLeave={() => setIsHover(false)}
      style={{
        backgroundColor: isHover ? '#ddd' : '#fff'
      }}
    >
      {items.map(story => (
        <li key={story.id}>
          {story.label}
        </li>
      ))}
    </ul>
  );
}

请注意,每次将鼠标悬停在 StoryTray 组件上时,“创建故事”都会再次添加到列表中。代码的意图是在最后添加一次。但是 StoryTray 直接修改了 props 中的 stories 数组。每次 StoryTray 渲染时,它都会在同一个数组的末尾再次添加“创建故事”。换句话说,StoryTray 不是纯函数——多次运行它会产生不同的结果。

要解决此问题,你可以创建一个数组的副本,并修改该副本而不是原始副本

export default function StoryTray({ stories }) {
const items = stories.slice(); // Clone the array
// ✅ Good: Pushing into a new array
items.push({ id: 'create', label: 'Create Story' });

这将 使 StoryTray 函数成为纯函数。每次调用它时,它只会修改数组的新副本,而不会影响任何外部对象或变量。这解决了这个错误,但你必须让组件更频繁地重新渲染,然后才能发现它的行为有问题。

在最初的例子中,这个错误并不明显。现在让我们将原始(有错误的)代码包装在 <StrictMode>

export default function StoryTray({ stories }) {
  const items = stories;
  items.push({ id: 'create', label: 'Create Story' });
  return (
    <ul>
      {items.map(story => (
        <li key={story.id}>
          {story.label}
        </li>
      ))}
    </ul>
  );
}

**严格模式*总是*调用你的渲染函数两次,因此你可以立即看到错误**(“创建故事”出现两次)。这使你能够在流程的早期发现此类错误。当你修复组件以在严格模式下渲染时,你*也*修复了许多可能的未来生产错误,例如之前的悬停功能

import { useState } from 'react';

export default function StoryTray({ stories }) {
  const [isHover, setIsHover] = useState(false);
  const items = stories.slice(); // Clone the array
  items.push({ id: 'create', label: 'Create Story' });
  return (
    <ul
      onPointerEnter={() => setIsHover(true)}
      onPointerLeave={() => setIsHover(false)}
      style={{
        backgroundColor: isHover ? '#ddd' : '#fff'
      }}
    >
      {items.map(story => (
        <li key={story.id}>
          {story.label}
        </li>
      ))}
    </ul>
  );
}

如果没有严格模式,你很容易错过这个错误,直到你添加了更多的重新渲染。严格模式使相同的错误立即出现。严格模式可帮助你在将错误推送到你的团队和用户之前找到它们。

阅读有关保持组件纯净性的更多信息。

注意

如果您安装了 React 开发者工具,则在第二次渲染调用期间,任何 console.log 调用都会显得略微暗淡。React 开发者工具还提供了一个设置(默认关闭)以完全抑制它们。


修复在开发过程中重新运行 Effects 发现的错误

严格模式还可以帮助查找 Effects 中的错误。

每个 Effect 都有一些设置代码,并且可能还有一些清理代码。通常,React 在组件*挂载*(添加到屏幕)时调用设置,并在组件*卸载*(从屏幕中移除)时调用清理。如果自上次渲染以来其依赖项发生了更改,React 还会再次调用清理和设置。

当严格模式开启时,React 还会在开发过程中为每个 Effect 运行**一个额外的设置+清理周期**。这可能会让人感到惊讶,但它有助于揭示难以手动捕获的细微错误。

以下示例说明了在严格模式下重新运行 Effects 如何帮助您及早发现错误。

考虑这个将组件连接到聊天室的示例

import { createRoot } from 'react-dom/client';
import './styles.css';

import App from './App';

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

这段代码存在一个问题,但可能不是立即就能看出来。

为了使问题更明显,让我们实现一个功能。在下面的示例中,roomId 不是硬编码的。相反,用户可以从下拉列表中选择他们想要连接到的 roomId。点击“打开聊天室”,然后依次选择不同的聊天室。跟踪控制台中活动连接的数量

import { createRoot } from 'react-dom/client';
import './styles.css';

import App from './App';

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

您会注意到打开的连接数量一直在增长。在真实的应用程序中,这会导致性能和网络问题。问题在于 您的 Effect 缺少清理函数:

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]);

现在,您的 Effect 会在自身之后“清理”并销毁过时的连接,从而解决了泄漏问题。但是,请注意,在您添加更多功能(选择框)之前,问题并没有显现出来。

在最初的例子中,这个错误并不明显。现在让我们将原始(有错误的)代码包装在 <StrictMode>

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './styles.css';

import App from './App';

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

**使用严格模式,您可以立即看到存在问题**(活动连接的数量跃升至 2)。严格模式会为每个 Effect 运行一个额外的设置+清理周期。此 Effect 没有清理逻辑,因此它会创建一个额外的连接,但不会销毁它。这暗示您缺少一个清理函数。

严格模式可让您在流程早期就注意到此类错误。当您通过在严格模式下为 Effect 添加清理函数来修复它时,您*也*修复了许多未来可能出现的生产错误,例如之前的选择框

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './styles.css';

import App from './App';

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

请注意,控制台中活动连接计数如何不再持续增长。

如果没有严格模式,很容易就会错过您的 Effect 需要清理。通过在开发中为您的 Effect 运行*设置 → 清理 → 设置*而不是*设置*,严格模式使缺少的清理逻辑更加明显。

阅读有关实现 Effect 清理的更多信息。


修复由严格模式启用的弃用警告

如果 <StrictMode> 树中的任何组件使用以下任何已弃用的 API,React 就会发出警告

这些 API 主要用于较旧的 类组件,因此它们很少出现在现代应用程序中。