React 组件渲染在推送新 URL 时被多次调用

React component render is called multiple times when pushing new URL

本文关键字:URL 调用 组件 React      更新时间:2023-09-26

我正在构建一个PhotoViewer,当用户向左或向右按时,它会更改照片。我正在使用React,Redux,react-router和react-router-redux。当用户向左或向右按时,我做两件事,我使用 this.context.replace() 更新 url,并调度一个操作来更新当前查看的照片,this.props.dispatch(setPhoto(photoId)) .我正在订阅状态更改以进行调试。

上述每一行都会触发新的状态更改。调度操作会更新存储,因为我更新了currentlyViewedPhoto,更新 url 会更新存储,因为 react-router-redux 会更新存储中的 URL。当我调度操作时,在第一个重新渲染周期中,组件的render函数被调用两次。在第二个重新渲染周期中,组件的 render 函数被调用一次。这正常吗?以下是相关代码:

class PhotoViewer extends Component {
  pressLeftOrRightKey(e) {
    ... code to detect that left or right arrow was pressed ...
    // Dispatching the action triggers a state update
    // render is called once after the following line
    this.props.dispatch(setPhoto(photoId)) // assume photoId is correct
    // Changing the url triggers a state update
    // render is called twice
    this.context.router.replace(url) // assume url is correct
    return
  }
  render() {
    return (
      <div>Test</div>
    )
  }
}
function select(state) {
  return state
}
export default connect(select)(PhotoViewer)

渲染被调用三次正常吗?这似乎是矫枉过正,因为 React 将不得不做 DOM 差异三次。我想这并不重要,因为什么都没有改变。我是这个工具集的新手,所以请随时提出有关此问题的更多问题。

如果您在三个不同的阶段设置状态,那么组件也将重新渲染三次。

setState(( 将始终触发重新渲染,除非有条件 渲染逻辑在 shouldComponentUpdate(( 中实现。

(来源(

您可以在 shouldComponentUpdate(( 中实现逻辑,以防止在遇到性能问题时进行不必要的重新渲染。

我认为这是正常的。如果您没有明显的性能问题,我不会出汗。如果性能开始成为问题,如果您确定某些状态更改不会更改呈现的组件,则可以考虑重写shouldComponentUpdate生命周期方法。

编辑:如果您只需要在shouldComponentUpdate生命周期方法中进行浅层比较,您还可以考虑扩展React.PureComponent而不是React.Component。更多信息在这里。

这是一个老问题,我明白了,但我也注意到了这个问题。就像唐纳德的回答中提到的,这似乎很正常。我尝试了三件事来解决我的特定情况:

  • 我关闭了 React DevTools 插件,看看它是否是重复消息的原因。
  • 我检查了另一个浏览器,看看这是否是问题所在。
  • 我创建了一个简短的测试来检查它是否发生在简单的组件上。

前两种方法

禁用 React DevTools 插件不会更改记录到控制台的消息数。

更改浏览器也没有效果。我尝试了Chromium和Firefox。

第三种方法

即使是最简单的组件似乎也会多次调用生命周期方法。

给定两个文件,App.jscomponents/TestComponent.js ,如下所示:

// App.js
import React from 'react';
import './App.css';
import Test from './components/TestComponent';
function App()  {
  console.log('App render');
  return ( 
      <div>
        <Test />
      </div>
  
  );
}
export default App;
// ./components/TestComponent.js
import React, { Component } from 'react';

class Test extends Component {
  constructor(props) {
    console.log('Test constructed');
    super(props);
    
  }
  componentDidMount() {
    console.log('Test mounted');
  }
  componentDidUpdate() {
    console.log('Test updated');
  }
  render() {
    console.log('Test rendered');
    return (
      <div>
        <h1>Hello, world!</h1>
      </div>
    );
  }
}
export default Test;

测试的生命周期方法被多次调用。

笔记

  • 我使用创建-反应-应用程序来设置应用程序。
  • 反应版本是 16.13.1。

结果和讨论

使用yarn start,我在控制台中得到以下内容:

App render
TestComponent.js:7 Test constructed
TestComponent.js:7 Test constructed
TestComponent.js:20 Test rendered
TestComponent.js:20 Test rendered
TestComponent.js:12 Test mounted

至少对我来说,yarn注意到开发版本没有优化,运行yarn build然后python -m http.server --directory build提供它会得到一个没有重复消息的页面。

至于为什么你会得到三个渲染而不是两个,我不能说。我的建议是尝试构建应用程序,看看问题是否在生产版本中仍然存在。

如果还是遇到问题,也可以按照唐纳德的建议,在shouldComponentUpdate中实现逻辑。

如果使用>v16.3,请尝试删除 和它应该工作正常。

为什么?由于 v16.3 用于类组件和 v16.8 用于钩子,React 引入了 ,以便使过渡到 CurrentReact (在不久的将来(对开发人员更加友好,因为渲染阶段可以多次调用。

以下文章值得一读:

反应组件渲染两次

React Components 渲染两次,让我发疯

等等,你没有使用?!

注意:这仅适用于开发环境。

删除也适用于我的情况

您可以删除React.StrictMode以前:

   root.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );

自:以前:

   root.render(
        <App />
    );

文件默认值为index.js