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

IT技术 javascript reactjs react-router redux
2021-03-24 01:25:54

我正在构建一个 PhotoViewer,它可以在用户向左或向右按​​下时更改照片。我正在使用 React、Redux、react-router 和 react-router-redux。当用户向左或向右按​​下时,我会做两件事,我使用更新 urlthis.context.replace()并发送一个操作来更新当前查看的照片,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)

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

4个回答

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

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

来源

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

我没有亲自使用过 react-router-redux 所以我不能很遗憾地回答这个问题。您可以尝试记录商店内的状态shouldComponentUpdate变化,或者看看发生了什么变化。
2021-05-27 01:25:54
我可以理解是否有两个重新渲染(来自调度操作和 url 替换),但三个对我来说有点担心。url 替换会导致两次更新,而不是一次。react-router-redux 是否对商店进行了两次更新?
2021-06-16 01:25:54
我觉得这是站不住脚的。满足于“除非遇到问题,否则只需使用相同的参数和结果运行任何多次”。
2021-06-22 01:25:54

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

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

完全可以理解 - 对于这种情况,除非您拥有非常复杂的组件,否则您真的不会开始看到性能问题。
2021-05-29 01:25:54
我也是 React 的新手,很惊讶render()被调用了两次,哦顺便说一句,切换到PureComponent没有区别:(
2021-05-29 01:25:54
很高兴知道这是正常的。这是我第一次使用这种架构,我真的很担心渲染被多次触发。
2021-06-13 01:25:54

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

  • 我关闭了 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;

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

笔记

  • 我使用 create-react-app 来设置应用程序。
  • react版本是 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会提供一个没有重复消息的页面。

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

如果您仍然遇到问题,您也可以按照 Donald 的建议在shouldComponentUpdate.

如果使用 >v16.3,然后尝试删除<React.StrictMode>并且它应该可以正常工作。

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

以下文章值得一读:

React 组件渲染了两次

React Components 渲染两次,让我发疯

等等,你没有使用 <React.StrictMode>?!

注意:这仅适用于 dev env。