Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React component render is called multiple times when pushing new URL

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.

like image 949
egidra Avatar asked Feb 01 '16 17:02

egidra


People also ask

Why is my React component rendering multiple times?

This is because the React app component got re-rendered after the state values were modified, and it also re-rendered its child components.

How do you prevent too many re renders in React?

React limits the number of renders to prevent an infinite loop" occurs for multiple reasons: Calling a function that sets the state in the render method of a component. Immediately invoking an event handler, instead of passing a function. Having a useEffect hook that sets and re-renders infinitely.

How do you stop Rerendering of component in React hooks?

memo() If you're using a React class component you can use the shouldComponentUpdate method or a React. PureComponent class extension to prevent a component from re-rendering.

How often is render called React?

Generally, it gets called by React at various app stages when the React component instantiates for the first time, or when there is a new update to the component state. Render does not take any arguments and returns a JSX.


4 Answers

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.

like image 197
villeaka Avatar answered Oct 19 '22 06:10

villeaka


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.

like image 39
Donald Avatar answered Oct 19 '22 05:10

Donald


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.

like image 5
Randy Josleyn Avatar answered Oct 19 '22 05:10

Randy Josleyn


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.

like image 1
KJ Sudarshan Avatar answered Oct 19 '22 07:10

KJ Sudarshan