Home > database >  Why dependency useEffect is array state not inifinite loop
Why dependency useEffect is array state not inifinite loop

Time:10-28

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])

CodePudding user response:

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.

CodePudding user response:

Primers:

  • React runs the entire function line by line on every re-render. Essentially, all the variables and functions are reinitialized with brand new memory addresses. However, there's a catch, the variables and the functions that come from the React functions are not reinitialized with new addresses, for example, useState, useCallback (only re-runs when its dependencies are changed), etc
  • React re-renders whenever any state changes
  • To detect whether any state has changed, React runs an equality check between the previous state and the current state. Based upon the type (String, Number, Object) of the state, the checks behave differently
    • When 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 state
    • Consider an Object 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)
  • Every time an object is created, it is assigned a new memory address (reference)

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.

  • Related