React Components render 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

1function App() {
2 const [click, setClick] = React.useState(0);
4 console.log('I render 😡', click);
5 return (
6 <div>
7 <button onClick={() => setClick(click => click + 1)}>
8 Clicks: {click}
9 </button>
10 </div>
11 )

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

1I render 😡 0
2I render 😡 0

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

1I render 😡 1
2I render 😡 1

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

2 <React.StrictMode>
3 <App />
4 </React.StrictMode>,
5 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

1I render 😡 0
2I render 😡 1

B) Functional Component with useReduce

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

1function App() {
2 const [clicks, dispatch] = React.useReducer((state, action) => {
3 switch (action.type) {
4 case 'click':
5 return state + 1;
6 default:
7 throw new Error()
8 }
9 }, 0)
11 console.log('I render 😡', clicks);
12 return (
13 <div >
14 <button onClick={()=>dispatch({type:'click'})}>
15 Clicks: {clicks}
16 </button>
17 </div>
18 )

Browser console:

1I render 😡 0
2I render 😡 0
4I render 😡 1
5I render 😡 1

C) Class Component

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

1class App extends React.Component {
2 render() {
3 console.log('I render 😡');
4 return null;
5 }
Browser Console Output
[Browser Console Output]

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.

Write a response on Medium