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'));
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.
- On component mount , As the init state is empty, the console will print an empty string.
- 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:
- constructor
- componentWillMount (or UNSAFE_componentWillMount)
- componentWillReceiveProps (or UNSAFE_componentWillReceiveProps)
- componentWillUpdate (or UNSAFE_componentWillUpdate)
- getDerivedStateFromProps
- shouldComponentUpdate render
- 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:
- Initial props/state --> render --> DOM update --> mounted
- 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;
Here's a good diagram of the component lifecycle, it lists the class-based lifecycle functions, but the render/commit phases are the same.
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
, andshouldComponentUpdate
methods- Class component static
getDerivedStateFromProps
method- Function component bodies
- State updater functions (the first argument to
setState
)- Functions passed to
useState
,useMemo
, oruseReducer
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 withuseMemo
.
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
Get Mouse Wheel Events in Jquery
How to Chain Three Asynchronous Calls Using Jquery Promises
Iterate Through Nested JavaScript Objects
How to Download a File Using Window.Fetch
How to Replace Dom Element in Place Using JavaScript
Html5 Filereader How to Return Result
Putting HTML Inside an Iframe (Using JavaScript)
Play .Wav Sound File Encoded in Base64 with JavaScript
How to Create Checkbox Inside Dropdown
Change <Audio> Src with JavaScript
How to Automatically Allow Blocked Content in Ie
JavaScript Localstorage Object Broken in Ie11 on Windows 7
Passing a Variable from Node.Js to HTML
Change <Select>'s Option and Trigger Events with JavaScript