Why Does Usestate Cause the Component to Render Twice on Each Update

Why does useState cause the component to render twice on each update?

Your App component is rendered within React.StrictMode which is what causes your code to run in strict mode and in strict mode the consoles are shown twice because each function is made to run twice in development mode

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);

According to react docs:

Strict mode can’t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:

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

Why does react re-render twice when using the state hook (useState)?

What you're seeing is the first 'before' and 'after' is from the initial render and the second pair is from when you click the button and state is updated.

It seems the offending code comes from <React.StrictMode>

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

Removing the <React.StrictMode> gets rid of this issue.

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

Sample Image
Console

Further Reading: React Components rendered twice — any way to fix this?

Why useState cause the dom to render twice

The console statement is in function body, React will execute the function body on each render.

  1. On component mount , As the init state is empty, the console will print an empty string.
  2. As you are updating the the state on component Mount, React will execute the function body again and then log the state with the updated value.

As you are using the React.StrictMode it can render the component more than once. That is the reason you see the console logs multiple times.

The commit phase is usually very fast, but rendering can be slow. For this reason, the upcoming concurrent mode (which is not enabled by default yet) breaks the rendering work into pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption).
Render phase lifecycles include the following class component methods:

  1. constructor


    1. componentWillMount (or UNSAFE_componentWillMount)
    2. componentWillReceiveProps (or UNSAFE_componentWillReceiveProps)
    3. componentWillUpdate (or UNSAFE_componentWillUpdate)
    4. getDerivedStateFromProps
    5. shouldComponentUpdate render
    6. setState updater functions (the first argument)

Because the above methods might be called more than once, it’s important that they do not contain side-effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they can often be non-deterministic.

You can read more about React.StrictMode here

React component render twice using useState

Your app is working fine. It is rendering as it should. As we know:

A React component re-renders whenever its props or state change.

And react component lifecycle order is:

  1. Initial props/state --> render --> DOM update --> mounted
  2. props/state changed --> render --> DOM update --> updated ... so on

In the example below, it is rendering 2 times and that's correct:

  • First one (first console.log) is due to initial render with state as []
  • Second one (second console.log) is due to state change (caused by useEffect) to ['apple', 'banana']
function Events() {
const [allEvents, setAllEvents] = React.useState([]);
console.log('Event Rendered', allEvents);

useEffect(() => {
setAllEvents(['apple', 'banana']);
}, []);

return <>Events</>;
}

About using React.memo:

React.memo only checks for props changes. If your function component wrapped in React.memo has a useState or useContext Hook in its implementation, it will still rerender when state or context change.

You can not skip re-render using React.memo due to change in state. You can only optimize to skip re-rendering caused by change in props.

But in the example above, you don't have props passed from the parent component, the only props passed to Events are those passed by react-router i.e. route props. So, there is no need to use React.memo.

Here is sandbox, check the console.logs. You will see only 3 logs: "App render", "Event render with initial state", "Event render with new state".



EDIT:

If we remove StrictMode from index.html, and add below console.logs in components:

App.js --> console.log('App rendered')
Evenets.js --> console.log('Event rendered', allEvents, isLoading) // (allEvents and isLoading are state variables here)

And go to http://localhost:3000, we see 1 log:

App Rendered

Now click on "Events", we see 3 logs:

1: Event Rendered, [], true
2: Event Rendered, [{}, ... 54 items], true
3: Event Rendered, [{}, ... 54 items], false

which is correct behavior (refer lifecycles order written above):

  • 1st log: render with initial state ([], true)
  • 2nd log: render with new allEvents (54 items) and old isLoading (true)
  • 3rd log: render with old allEvents (54 items) and new isLoading (false)

Below are the right questions to ask now:

Question1:

Why 2nd and 3rd render (log) are separate, should not they be batched (merged) and applied together as they are written in the same function?

fetch('url').then(() => {
// ... code here
setAllEvents([...events])
setLoading(false)
})

Answer:

No, they will not be batched in above code. As explained by Dan Abramov:

This is implementation detail and may change in future versions.

In current release, they will be batched together if you are inside a React event handler. React batches all setStates done during a React event handler, and applies them just before exiting its own browser event handler.

With current version, several setStates outside of event handlers (e.g. in network responses) will not be batched. So you would get two re-renders in that case.

There exists a temporary API to force batching. If you write ReactDOM.unstable_batchedUpdates(() => { this.fn1(); }); then both calls will be batched. But we expect to remove this API in the future and instead batch everything by default.

So, you can write (inside fetch's then), if you want, it will save 1 render:

ReactDOM.unstable_batchedUpdates(() => {
setAllEvents([...events])
setLoading(false)
})

Question2:

What's React event handler in above quote?

Answer: foo in example below. These 2 set states will be batched.

const foo = () => {
setAllEvents([
{ _id: '5ede5af03915bc469a9d598e', title: 'jfklsd', },
])
setLoading(false)
}

<button onClick={foo}>CLICK</button>

Question3:

Does it update HTML DOM as many times as it renders (prints console.log)?

Answer: No. React compares calculated virtual DOMs before updating real DOM, so only those changes are applied to real DOM which are required to update the UI.

Question4:

Why was rendering doubled when we use StrictMode?

Answer: Yes, StrictMode will intentionally double invoke "render" and some other lifecycle methods to detect side-effects. Strict mode checks are run in development mode only; they do not impact the production build.

React useState cause double rendering

Put the console.log in an useEffect hook without dependencies and you'll see it isn't actually rendering twice.

import React, { useEffect, useState } from 'react';

const MyComponent = () => {
const [count, setCount] = useState(0);

useEffect(() => {
console.log(count);
});

return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
count: {count}
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};

export default MyComponent;

Edit wonderful-tesla-cf8uu

Here's a good diagram of the component lifecycle, it lists the class-based lifecycle functions, but the render/commit phases are the same.

Sample Image

The import thing to note is that the component can be "rendered" without actually being committed (i.e. the conventional render you see to the screen). The console.log alone is part of that. The effects run after in the "commit" phase.

useEffect

... The function passed to useEffect will run
after the render is committed to the screen. ...

By default, effects run after every completed render, ...

React Strict Mode

Detecting Unexpected Side-effects

Strict mode can’t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:

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

This only applies to development mode.

react functional component render twice after setState in the effect

The issue is that you are unconditionally updating state that you are using as a dependency.

This behavior isn't unexpected.

See Bailing out of a state update

If you update a State Hook to the same value as the current state,
React will bail out without rendering the children or firing effects.
(React uses the Object.is comparison algorithm.)

Note that React may still need to render that specific component again
before bailing out. That shouldn’t be a concern because React won’t
unnecessarily go “deeper” into the tree. If you’re doing expensive
calculations while rendering, you can optimize them with useMemo.

The count is 1 on the initial render, the first log.
useEffect hook runs and enqueues a state update and the update is processed.

The count is 2 on the next render, the second log.
count state value updated so useEffect hook callback runs and enqueues another state update to the same value and the update is processed.

The count is still 2 on the next render, the third log.
count state hasn't changed so the useEffect hook callback isn't triggered.

Why App or Quiz component rendered two times?

This is because you are using StrictMode, which will cause multiple renders. This in intentionally done to help encourage developers use side effects properly. The double render will only happen in development and not in production.

Also see this answer for more information.

Strict mode is on because you have your components wrapped with <React.StrictMode>:

<React.StrictMode>
<Router>
...
...
</React.StrictMode>

React component rendering twice when using useState hook

Check this out : https://github.com/facebook/react-devtools/issues/1297

The "unexpected re-render" isn't actually caused by useEffect
specifically– but rather, it's the way DevTools "inspects" hooks
values by re-rendering the function component in isolation.

While I understand that unexpected renders can indicate problems in
some cases, this particular one shouldn't actually be a problem for
several reasons:

The renders aren't recursive. (Child components aren't rendered.) The
renders only happen for users with DevTools installed, and even then–
only impact a single component (the one currently selected in the
tree). The renders don't have side effects (e.g. the DOM won't be
updated).



Related Topics



Leave a reply



Submit