React component render is called multiple times when pushing new URL

85,059

Solution 1

I would assume that this is normal. If you aren't having noticeable performance issues, I wouldn't sweat it. If performance begins to be a problem, you can look into overriding the shouldComponentUpdate lifecycle method if you are certain that certain state changes won't change the rendered component.

Edit: You could also look into extending React.PureComponent instead of React.Component if you only need shallow comparison in your shouldComponentUpdate lifecycle method. More info here.

Solution 2

If you are setting state at three different stages then the component will re-render three times as well.

setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate().

(source)

You can implement logic in shouldComponentUpdate() to prevent unneeded re-renders if you are experiencing performance issues.

Solution 3

This is an old question, I see, but I also noted this issue. Like noted in Donald's answer, it seems to be normal. I tried three things to troubleshoot my specific case:

  • I turned off the React DevTools plugin to see if it was the cause of the duplicate messages.
  • I checked a different browser to see if that was the issue.
  • I created a short test to check if it happens on simple components.

The first two methods

Disabling the React DevTools plugin did not change the number of messages logged into the console.

Changing browser also had no effect. I tried Chromium and Firefox.

The third method

Even the simplest components seem to call the life cycle methods more than once.

Given two files, App.js and components/TestComponent.js, as follows:

// 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;

Life cycle methods of Test are called multiple times.

Notes

  • I used create-react-app to set up the app.
  • The react version is 16.13.1.

Results and discussion

With yarn start, I get the following in the console:

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

For me, at least, yarn notes that the development build is not optimized, and running yarn build and then serving it with python -m http.server --directory build gives a page that doesn't have the duplicate messages.

As for why you're getting three renders instead of two, I can't say. My suggestion is to try building the app and seeing if the problem persists in the production build.

If you still encounter the issue, you can also go with Donald's suggestion to implement logic in shouldComponentUpdate.

Solution 4

If using >v16.3, then try removing <React.StrictMode> and it should work fine.

Why? Since v16.3 for class components and v16.8 for hooks , React introduced <React.StrictMode> in order to make the transition to Concurrent React ( in the near future ) more developer friendly, since render phase can be invoked multiple times then.

The following articles are read worthy:

React Components rendered Twice

React Components render twice and drive me crazy

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

Note: This is applicable to dev env only.

Share:
85,059

Related videos on Youtube

egidra
Author by

egidra

Updated on July 09, 2022

Comments

  • egidra
    egidra almost 2 years

    I am building a PhotoViewer that changes photos when the user presses left or right. I am using React, Redux, react-router, and react-router-redux. When the user presses left or right, I do two things, I update the url using this.context.replace() and I dispatch an action to update the currently viewed photo, this.props.dispatch(setPhoto(photoId)). I am subscribing to state changes for debugging.

    Each of the above lines triggers a new state change. Dispatching an action updates the store since I update the currentlyViewedPhoto and updating the url updates the store because react-router-redux updates the url in the store. When I dispatch the action, in the first rerendering cycle, the component's render function gets called twice. In the second rerendering cycle, the component's render function gets called once. Is this normal? Here is the relevant code:

    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)
    

    Is this normal that render is called three times? It seems like overkill because React will have to do the DOM diffing three times. I guess it won't really matter because nothing has changed. I am new to this toolset, so feel free to ask any more questions about this problem.

    • Marc Stober
      Marc Stober over 7 years
      I had an issue with not having slashes in my links. If I had <Link to="foo">, React router (not using redux) was "redirecting" from #foo to #/foo and causing an extra render. Adding the slash to the link fixed it. This is using hashHistory.
  • egidra
    egidra over 8 years
    I could understand if there were two re-renders (from the dispatch action and the url replacement), but three is kind of concerning to me. The url replacement causes two updates, instead of one. Does react-router-redux make two updates to the store or something?
  • egidra
    egidra over 8 years
    That's good to know that it is normal. This is my first time using this architecture, and it was really concerning to me that render was being fired multiple times.
  • villeaka
    villeaka over 8 years
    I haven't personally used react-router-redux so I can't unfortunately answer that. You could try logging state mutations inside your store or shouldComponentUpdate and see what's changing.
  • Donald
    Donald over 8 years
    Totally understandable - for a situation like this, you really won't begin to see performance issues until you have very complex components.
  • grantwparks
    grantwparks almost 8 years
    I find this to be indefensible. Complacency with "just run whatever however many times with the same arguments and results unless you get a problem".
  • Dexygen
    Dexygen over 5 years
    I too am new to React and was surprised that render() was getting called twice, and oh BTW switching to PureComponent made no difference :(
  • adrian
    adrian over 2 years
    Repeated answer
  • kirjosieppo
    kirjosieppo over 2 years
    This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review