React Components rendered twice — any way to fix this?

Many developer have implemented a similar functional component and have seen this behavior. Some have even opened a bug report in the official React repository.

The reason why this happens is an intentional feature of the React.StrictMode. It only happens in development mode and should help to find accidental side effects in the render phase.

Let’s find out if there is a way to avoid this problem by trying different implementations.

A) Functional Component with useState

function App() {
const [click, setClick] = React.useState(0);
console.log('I render 😡', click);
return (
<div>
<button onClick={() => setClick(click => click + 1)}>
Clicks: {click}
</button>
</div>
)
}

if you used create-react-app and run your app with yarn start you will see the following output in the browser console:

I render 😡 0
I render 😡 0

if you click the button once, another two lines will be appended to the log:

I render 😡 1
I render 😡 1

The reason for this is the React.StrictMode wrapper in the index.js file:

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

Removing the wrapper <React.StrictMode> will solve the problem in development mode. The same is true, if you build the app for production with yarn build && npx serve -p 3001 build

I render 😡 0
I render 😡 1

B) Functional Component with useReduce

I replaced useState with useReduce and the result was the same:

function App() {
const [clicks, dispatch] = React.useReducer((state, action) => {
switch (action.type) {
case 'click':
return state + 1;
default:
throw new Error()
}
}, 0)
console.log('I render 😡', clicks);
return (
<div >
<button onClick={()=>dispatch({type:'click'})}>
Clicks: {clicks}
</button>
</div>
)
}

Browser console:

I render 😡 0
I render 😡 0
I render 😡 1
I render 😡 1

C) Class Component

Switching to Class Comonents is no difference - you will see the effect even without any state management:

class App extends React.Component {
render() {
console.log('I render 😡');
return null;
}
}

What is happening behind the scenes?

The main reason is React.StrictMode which was introduced in version 16.3.0. Back then it only could be used for class components. With the release of 16.8.0 it is also applied for hooks.

StrictMode is a tool for highlighting potential problems in an application. Like Fragment, StrictMode does not render any visible UI. It activates additional checks and warnings for its descendants. ‒ official React Documentation

React.StrictMode currently helps you with:

In most cases it will throw an error message into the browser console log. It fails to automatically detect render side effects as they often can be non-deterministic behavior.

To detect side effects the following functions are invoked twice:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies
  • State updater functions
  • Functions passed to useState, useMemo, or useReducer (any Hook)

Why should I use React.StrictMode?

React will soon provide a new Concurrent Mode which will break render work into multiple parts. Pausing and resuming the work between this parts should avoid the blocking of the browsers main thread.

This means that the render phase could be invoked multiple times and should be a Pure Function without any side effects!

If you did not used React.StrictMode because you started in the early React days or have been annoyed by the double rendering of your components I hope that I was able to convince you to use it now to aviod any surprise when switching to concurrent mode in the near future.

Originally published at https://www.heissenberger.at.

Fast-track professional successful in the design, development and deployment of technology strategies and policy. Experienced leading Internet and IS operations

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store