Skip to content

React Strict Mode 导致组件重复渲染问题

问题现象

在使用 React 开发时,你可能会发现组件在开发环境中渲染了两次,控制台中的日志也重复输出。这种奇怪的行为通常会让开发者感到困惑,特别是当涉及到 API 调用或其他副作用操作时。

通过调试,你可能会发现问题的根源在于 React 的严格模式(Strict Mode):

js
if (workInProgress.mode & StrictMode) {
  instance.render();
}

什么是 React Strict Mode?

React Strict Mode

React 严格模式是一个用于突出显示应用程序中潜在问题的工具。它不会渲染任何可见的 UI,但它会为其后代元素激活额外的检查和警告。

严格模式在开发环境下有意将以下函数双重调用:

  • 类组件的 constructor、render 和 shouldComponentUpdate 方法
  • 函数组件体
  • useState、useMemo 和 useReducer 的初始化函数

这种双重调用的目的是帮助识别意外的副作用,确保你的组件是纯函数。

为什么不应该禁用 Strict Mode?

虽然严格模式会导致组件重复渲染,但它实际上是一个非常有价值的开发工具:

严格模式的好处

  • 识别不安全的生命周期方法使用
  • 警告过时的 API 使用
  • 检测意外的副作用
  • 确保组件是可预测和可测试的

解决方案

1. 接受并适应严格模式(推荐)

最佳实践是接受严格模式的行为并相应地调整你的代码。这样可以确保你的应用更加健壮且符合 React 最佳实践。

处理 API 调用问题

如果你的 API 调用会因为重复渲染而产生额外成本,可以使用计数器来跳过第一次调用:

js
let globalFetchCount = 0;

const MyComponent = () => {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    globalFetchCount++;
    
    // 在开发环境的严格模式下跳过第一次调用
    if (globalFetchCount === 1 && process.env.NODE_ENV === 'development') {
      console.log('跳过严格模式下的第一次 API 调用');
      return;
    }
    
    // 实际的 API 调用
    fetchData().then(setData);
  }, []);
  
  return <div>{/* 组件内容 */}</div>;
};

使用 React 开发者工具

React 开发者工具浏览器扩展提供了隐藏严格模式下第二次渲染日志的选项:

  1. 安装 React Developer Tools
  2. 打开 Chrome 开发者工具,切换到 "Components" 标签
  3. 点击齿轮图标进入设置
  4. 在 "Debugging" 选项卡中勾选 "Hide logs during second render in Strict Mode"

2. 禁用 Strict Mode(不推荐)

如果你确实需要禁用严格模式,可以根据你的项目类型进行操作:

Create React App 项目

src/index.js 中移除 <React.StrictMode> 包装:

js
// 修改前
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// 修改后
ReactDOM.render(
  <App />,
  document.getElementById('root')
);

Next.js 项目

next.config.js 中禁用严格模式:

js
module.exports = {
  reactStrictMode: false,
}

警告

禁用严格模式意味着你将失去它在开发过程中提供的宝贵警告和错误检测功能。这可能导致生产环境中出现难以调试的问题。

理解 Strict Mode 的工作原理

严格模式的双重渲染机制是为了帮助你发现组件中的副作用问题。React 会故意在开发环境中调用两次某些函数,以确保它们没有副作用。

这种机制可以帮助发现以下问题:

  • 在渲染期间修改 state
  • 不纯的渲染函数
  • 不稳定的生命周期方法使用

最佳实践

  1. 保持严格模式启用:尽管它会导致重复渲染,但它提供的警告和错误检测非常有价值
  2. 设计无副作用的组件:确保你的组件渲染是纯粹的操作,不修改外部状态
  3. 合理处理副作用:将副作用操作(如 API 调用)放在适当的位置(useEffect)
  4. 使用开发工具:利用 React 开发者工具来管理严格模式下的日志输出

总结

React Strict Mode 导致的组件重复渲染是故意设计的行为,目的是帮助开发者识别和修复潜在问题。虽然这可能会在开发过程中带来一些不便,特别是当涉及到昂贵的操作(如 API 调用)时,但通过适当的处理方式,你可以在享受严格模式带来的好处的同时,避免不必要的副作用。

记住,严格模式只影响开发环境,在生产构建中不会导致重复渲染。因此,最好的策略是适应这种行为并相应调整你的代码,而不是完全禁用这个有价值的开发工具。