I wrote a simple react to test how React render:
import ReactDOM from 'react-dom';
import React, { useState, useEffect } from 'react';
const App = (props) => {
// State
const [trackIndex, setTrackIndex] = useState(1);
const [trackProgress, setTrackProgress] = useState(0);
const clickHandler = () =>{
setTrackIndex((trackIndex)=>trackIndex 1)
}
useEffect(() => {
console.log("effect; trackIndex=",trackIndex)
console.log("effect; trackProgress=",trackProgress)
if(trackIndex<5){
console.log("set trackProgress")
setTrackProgress(trackIndex)
}
});
console.log("render")
return (
<div>
<p>{trackIndex}</p>
<p>{trackProgress}</p>
<button onClick={clickHandler}>Click me</button>
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById('root')
);
The following is the console output:
render
effect; trackIndex= 1
effect; trackProgress= 0
set trackProgress
render
effect; trackIndex= 1
effect; trackProgress= 1
set trackProgress
render
It seems that React renders three times before I click the button. The last rendering really confuses me. Could anyone explain to me why this render occurs and why no effect runs after this rendering? Thank you in advance
CodePudding user response:
It seems that your useEffect
is setting the state with the setTrackProgress
on the initial render of the component. This is because trackIndex
starts at 1
which is lower than 5. (seen by the if(trackIndex<5)
) Notice that you haven't provided array dependency to useEffect
as the second parameter. According to react-documentation this means the effect will only occur once after the first initial render of the component.
Also, I would suggest adding useCallback
to your clickHandler
in order to prevent re-defining the function for every render. (According to react-documentation)
CodePudding user response:
You're practically making an infinite loop, but React saves you. The first render
log is the initial render, then the useEffect
is executed and your component re-renders, leading to the 2nd render
log and the first effect; ...
log. Now a 2nd useEffect
is executed. However, the value of trackIndex
hasn't changed, so your if statement will evaluate true and updates trackProgress
with the same state/value(1). This leads to another re-render and the third render
log. Now, you would think that a 3rd useEffect
would be executed, but React is smart enough to know when state hasn't changed and thus doesn't execute the useEffect
.
Add dependencies to your useEffect
as stated above. That'll solve your problem.
useEffect(
...
, [trackIndex])