推送新URL时,会多次调用React组件呈现

时间:2016-02-01 17:37:44

标签: javascript reactjs react-router redux

我正在构建一个PhotoViewer,可在用户向左或向右按​​下时更改照片。我正在使用React,Redux,react-router和react-router-redux。当用户向左或向右按​​下时,我会做两件事,我使用this.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)

这是正常的渲染被调用三次吗?这看起来有点矫枉过正,因为React必须做三次DOM差异。我想这真的很重要,因为没有任何改变。我是这个工具集的新手,所以请随时再问这个问题。

4 个答案:

答案 0 :(得分:18)

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

  

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

source

您可以在shouldComponentUpdate()中实施逻辑,以防止在遇到性能问题时不需要重新渲染。

答案 1 :(得分:18)

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

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

答案 2 :(得分:4)

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

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

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

注释

  • 我使用create-react-app来设置应用程序。
  • 反应版本是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中实现逻辑。

答案 3 :(得分:1)

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

为什么?由于v16.3用于类组件,v16.8用于钩子,因此React引入了 ,以便在不久的将来向并发React过渡。 / em>,因为渲染阶段可以被多次调用。

以下文章值得阅读:

React Components rendered Twice

React Components render twice and drive me crazy

Wait, you're not using <React.StrictMode>?!

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