Reputation: 21
Why is Case 1 and infinite loop, whereas Case 2 isn't, as obj
in the useState()
isn't?
Case 1: Infinite loop
const [count, setCount] = useState(0)
const [obj,setObj] = useState({a:1, b:2})
useEffect(( )=>
{setCount((prevCount) => prevCount + 1)
},[{a:1, b: 2 }])
Case 2: Not an infinite loop
const [count, setCount] = useState(0)
const [obj,setObj] = useState({a:1, b:2})
useEffect(( )=>
{setCount((prevCount) => prevCount + 1)
},[obj])
Upvotes: 2
Views: 62
Reputation: 409
Primers:
useState
, useCallback
(only re-runs when its dependencies are changed), etcString
, Number
, Object
) of the state, the checks behave differently
Number
states are compared with ===
, their values are compared. Note that this is a constant time O(1)
operation. For instance, if previousState = 5
and currentState = 6
, React signals a change in stateObject
state with previousState = {a: 1, b: 2}
and currentState = {a: 2, b: 1}
. To understand whether the two states are different, every key must be checked to come to
a conclusion. However, note that this is an expensive opeartion with ~O(keys)
time complexity for flat objects (think about deeply nested objects and the time it would take?!). Since React has to run the equality checks often, it avoids this operation and goes for a referential check. Now, what does that mean and how is it faster? Every object in JavaScript has a memory address (a.k.a reference). React checks whether the two objects are equal based upon their reference with ===
in constant time O(1)
Why is there an infinite loop?
In the first example, the useEffect
runs for the first time and updates the count
state. This leads to a re-render. In the re-render, every line is executed again. When it comes to the useEffect
, React compares the previous {a: 1, b: 2}
with current {a: 1, b: 2}
based upon its equality check. Since both the objects are created on the fly, they both have different addresses and thus React thinks the dependency has changed and re-renders the component. This goes on and on and we're stuck in a infinite loop.
In the second example, obj
, a React state, is passed to the useEffect
. Since React doesn't reinitialize useState
in the re-render, the object is never reinitialized and there's no change in the memory address (reference). Thus, when the equality check is run by React, the objects come out to be equal and do not lead to any re-render.
Upvotes: 1
Reputation: 189
React uses referential equaility to check whether dependencies have changed in the first example, each time a new object is being created so reference changes and component rerenders.
In second case the state holds the reference of the object, that reference is persisted across rerenders so the useEffect is not triggered again.
Upvotes: 4