我的 React 组件渲染两次


许多使用现代 React 的前端开发人员不时地试图弄清楚为什么他们的组件在开发过程中渲染了两次。

其他人已经注意到这种行为,但他们认为这就是幕后React运作的方式,而有些人甚至在React官方存储库中打开了票证,将其报告为错误。

所以社区里肯定有一些关于这个的困惑😬

所有这些发生的原因是React.StrictMode

让我们深入研究一些真实的例子以复制这一点,然后首先调查为什么会发生这种情况。

#带有函数组件的示例

我们可以从运行一个全新的CRA安装开始:

npx create-react-app my-app && cd my-app

猛击

我们App.js通过添加一个非常简单的console.log语句来稍微调整一下:

function App() {
  console.log('I render 😁');

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
      </header>
    </div>
  );
}

反应 JSX

现在我们可以启动我们的应用程序yarn starthttp://localhost:3000在浏览器中打开:

我的 React 组件渲染两次,让我发疯

嗯,这条I render 😁语句只打印了一次,所以我们不能用一个死的简单函数组件来重现双重渲染。

#带有状态的函数组件的示例

但是当我们使用 React 钩子并向我们的函数组件添加一些状态管理时会发生什么?

function App() {
  const [clicks, setClicks] = React.useState(0);

  console.log('I render 😁');

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />

        <button onClick={() => setClicks(clicks => clicks + 1)}>
            Clicks: {clicks}
        </button>
      </header>
    </div>
  );
}

反应 JSX

让我们再次检查浏览器:

我的 React 组件渲染两次,让我发疯

我们来了!!所以它一开始渲染两次,然后每次我们点击我们添加的按钮时它都会渲染两次。

显然,React.useState影响了我们组件关于重新渲染的行为。

#带有生产状态的功能组件的示例

生产包呢?为了检查这一点,我们需要先构建我们的应用程序,然后我们可以使用像serve端口 3000这样的包来为它提供服务:

yarn build && npx serve build -l 3000

猛击

http://localhost:3000再次在浏览器中打开:

我的 React 组件渲染两次,让我发疯

呼!!!调试语句在开始时打印一次,每次单击按钮时都会打印一次。

正如我们所看到的,尽管我们通过使用React.useState.

#为什么会这样?

如上所述,原因是React.StrictMode. 如果我们检查src/index.js我们之前启动的应用程序中的文件CRA,我们会看到我们的<App />组件被它包裹了:

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

反应 JSX

显然,重新渲染的东西绝对不是错误,也不是与库的渲染机制有关的东西。相反是React🤗提供的调试机制

#什么是 React.StrictMode?

React.StrictMode是2018 年16.3.0版本中引入的包装器。起初,它仅应用于类组件,16.8.0之后它也应用于钩子。

如发行说明中所述:

React.StrictMode 是一个包装器,用于帮助为异步渲染准备应用程序

因此,它旨在帮助工程师避免常见的陷阱,并React通过删除遗留 API 来逐步升级他们的应用程序。

这些提示对于更好的调试非常有帮助,因为库正在走向异步渲染时代,因此不时会发生重大变化。

多么有用,对吧?

#为什么要双重渲染呢?

我们从React.StrictMode使用中获得的好处之一是它可以帮助我们检测渲染阶段生命周期中的意外副作用。

这些生命周期是:

  • constructor
  • componentWillMount (或 UNSAFE_componentWillMount)
  • componentWillReceiveProps (或 UNSAFE_componentWillReceiveProps)
  • componentWillUpdate (或 UNSAFE_componentWillUpdate)
  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • setState 更新程序函数(第一个参数)

所有这些方法都被多次调用,因此避免在它们中产生副作用很重要。如果我们忽略这个原则,很可能会导致不一致的状态问题和内存泄漏。

React.StrictMode 不能立即发现副作用,但它可以通过有意调用两次某些关键函数来帮助我们找到它们。

这些功能是:

  • 类成分constructorrendershouldComponentUpdate方法
  • 类组件静态getDerivedStateFromProps方法
  • 函数组件体
  • 状态更新器函数(的第一个参数setState
  • 函数传递给useStateuseMemouseReducer

这种行为肯定会对性能产生一些影响,但我们不必担心,因为它只发生在开发中而不是生产中。

这就是为什么我们只能在开发中为使用React.useState.


原文链接:https://codingdict.com/