export function App(props) {
const [number,setNumber] = useState(0);
console.log(number,'one')
useEffect(()=>{
console.log(number,'two')
setNumber(1);
},[number]);
console.log(number,'three')
return (
<div className='App'>
{number}
</div>
);
}
const { useState, useEffect } = React;
function App(props) {
const [number,setNumber] = useState(0);
console.log(number,'one')
useEffect(()=>{
console.log(number,'two')
setNumber(1);
},[number]);
console.log(number,'three')
return (
<div className='App'>
{number}
</div>
);
}
ReactDOM.createRoot(document.querySelector("#root")).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>
Given the code as above, I expected the output of console.log to be as follows
Expected output
0 one
0 three
0 two
1 one
1 three
1 two
Because during the initial rendering, the value of 'number' changed to 1. In the subsequent renderings, even if setNumber(1) is called, I expected 'number' not to change, so I thought there would be no re-rendering.
However, the actual output was as follows
Actual output
0 one
0 three
0 two
1 one
1 three
1 two
1 one
1 three
I was under the impression that if the state values are the same, no re-render would occur. However, even though the state values did not change, why is there an additional re-render happening?
This sequence can be explained by the following order of events.
App
component renders with the initial value 0
for number
. This causes the output 0 one
and 0 tree
.useEffect
callback is fired. This causes the output 0 two
.useEffect
called setNumber
(from 0
to 1
) React will register a state updated and re-render the component. Causing the output 1 one
and 1 tree
.useEffect
number
dependency changed from 0
to 1
the useEffect
callback is invoked again. Causing the output 0 two
.useEffect
called setNumber
(from 1
to 1
) React will register a state update (even though you use the same value) and re-render the component. Causing the output 1 one
and 1 tree
.useEffect
callback is not called this time because the dependency number
did not change. So this is where the render stops.If you want to avoid the last render, you should not call setNumber
if the value is already what it should be.
// avoid `setNumber(1)` if `number` is already `1` to prevent the final render
if (number != 1) setNumber(1);
const { useState, useEffect } = React;
function App(props) {
const [number,setNumber] = useState(0);
console.log(number,'one')
useEffect(()=>{
console.log(number,'two')
if (number != 1) setNumber(1);
},[number]);
console.log(number,'three')
return (
<div className='App'>
{number}
</div>
);
}
ReactDOM.createRoot(document.querySelector("#root")).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>