React.StrictMode 会导致多次渲染

本贴最后更新于 1620 天前,其中的信息可能已经事过境迁

React.StrictMode 会导致多次渲染

故事

今天在研究一个第三方库的时候发现一个很简单的组件在 render 时被渲染了两次。精简后的组件代码大致如下:

// App.js
import React, { useState } from 'react';

function App() {
  const [count] = useState(666);
  console.log('render!!');
  return (<div>{count}</div>);
}

export default App;


// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

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

在控制台中会看到输出了两次 render!!,使用的包版本如下:

{
  "react": "^16.13.1",
  "react-dom": "^16.13.1",
  "react-scripts": "3.4.1"
}

仔细看了 React 的文档以及参考了很多 Google 上的问题,发现是因为使用了 React.StrictMode render 导致的。

在开发环境中使用 StrictMode 的目的是为了:

  1. 识别不安全的生命周期
  2. 检测过时的 API 和废弃的方法
  3. 检测意外的副作用

React 在检测意外的副作用时可能重复调用某些生命周期方法、hooks 或者 render 方法,其包括:

  1. class 组件的 constructorrender 以及 shouldComponentUpdate 方法
  2. class 组件的生命周期方法 getDerivedStateFromProps
  3. 函数组件
  4. 状态更新函数 (即 setState 的第一个参数)
  5. 函数组件中 useStateuseMemo 或者 useReducer 中的函数

当 React 发现在重复调用这些方法时出现了内存泄漏、无限循环或者其他奇怪的表现时会在控制台输出错误,以供程序员快速定位错误。

Github 的某条 issue 中,Dan Abramov 也提到:

It is expected that setState updaters will run twice in strict mode in development. This helps ensure the code doesn’t rely on them running a single time (which wouldn’t be the case if an async render was aborted and alter restarted). If your setState updaters are pure functions (as they should be) then this shouldn’t affect the logic of your application.

简单来说就是我们在使用 hooks 或者在某些生命周期函数中不应该使用有副作用的代码。在开发模式的 StrictMode 下,React 会帮助我们发现这些不好的代码并给予提示。

StrictMode 的这个重复调用的特性只使用于开发模式,在生产模式下不会触发多次调用。

案例

github/ReactTraining/react-media/modules/Media.js

89 行使用了 useState 并在 useState 中调用 setUpMQLs 用以注册事件监听器。由于在 StrictModeuseState 中的函数会被执行 2 次(或多次),因此会导致某些事件监听器没有被 cancel 而一直存在,最终由于内存泄漏而被 React 检测到,控制台会输出 Can't perform a React state update on an unmounted component(issue#139)。

参考资料

  1. Strict Mode
  2. Double-Invoke of State Functions in React
  • 研究
    12 引用 • 34 回帖
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    247 引用 • 1347 回帖
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 402 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...