I want someone to confirm my intuition on below problem.
My goal was to keep track (similar to here) of props.personIdentId
and when it changed, to call setSuggestions([])
.
Here is code:
function Identification(props) {
let [suggestions, setSuggestions] = useState([]);
let prevPersonIdentId = useRef(null)
let counter = useRef(0) // for logs
// for accessing prev props
useEffect(() => {
prevPersonIdentId.current = props.personIdentId;
console.log("Use effect", props.personIdentId, prevPersonIdentId.current, counter.current++)
});
// This props value has changed, act on it.
if (props.personIdentId != prevPersonIdentId.current) {
console.log("Props changed", props.personIdentId, prevPersonIdentId.current, counter.current++)
setSuggestions([])
}
But it was getting in an infinite loop as shown here:
My explanation why this happens is that: initially when it detects the prop has changed from null to 3, it calls setSuggestions which schedules a re-render, then next re-render comes, but previous scheduled useEffect didn't have time to run, hence the prevPersonIdentId.current didn't get updated, and it again enters and passes the if condition which checks if prop changed and so on. Hence infinite loop.
What confirms this intuition is that using this condition instead of old one, fixes the code:
if (props.personIdentId != prevPersonIdentId.current) {
prevPersonIdentId.current = props.personIdentId;
setSuggestions([])
}
Can someone confirm this or add more elaboration?
It looks like this is what's happening:
- On the first pass,
props.personId
isundefined
and prevPersonIdentId.current isnull
. Note thatif (props.personIdentId != prevPersonIdentId.current)
uses!=
soundefined
is coerced tonull
and you don't enter the if. - Another render occurs with the same conditions.
props.personId
now changes, so you enter the if.setSuggestions([])
is called, triggering a re-render.- loop forever
Your useEffect
is never invoked, because you keep updating your state and triggering re-renders before it has a chance to run.
If you want to respond to changes in the prop, rather than attempting to roll your own change-checking, you should just use useEffect
with the value you want to respond to in a dependency array:
useEffect(() => {
setSuggestions([])
}, [props.personId] );