0

I was working with some code in React that had an infinite loop caused by useEffect and a function declared outside of the useEffect callback, after some research I found out that this is because on each re-render React creates a new inner function and the solution was to use the useCallback hook.

Still, I want to know why this is the behavior of objects (array, functions, objects) declared inside functions as opposed to values(strings, numbers, booleans, etc.) that seem to maintain their place in memory and aren't being created from scratch on every function call. I tried googling about it but didn't find anything.

Here's an example of what I'm referring to:

https://codesandbox.io/s/sweet-mccarthy-lf32r?file=/src/App.js

When you click the increase button, the effect that depends on the array, triggers, but the one depending on the number doesn't.

  • 2
    Does this answer your question? [Primitive value vs Reference value](https://stackoverflow.com/questions/13266616/primitive-value-vs-reference-value) – Drew Reese Oct 15 '20 at 06:10
  • Kinda, I suspected it had to do with that, but a piece to the puzzle was missing. After more research I think I have the answer haha. – Daniel Cruz Oct 15 '20 at 06:32

1 Answers1

1

Ok, I think I got the answer, but I would like to be corrected if I'm wrong:

So for this to make sense we need to understand 3 things:

  • Garbage collection
  • Primitive vs References
  • How equality comparisons work

First, to address my previous statement:

I want to know why this is the behavior of objects (array, functions, objects) declared inside functions as opposed to values(strings, numbers, booleans, etc.) that seem to maintain their place in memory and aren't being created from scratch on every function call.

Both primitives and references are created from scratch on every function call due to garbage collection, for more info check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management

Primitives are saved into the stack and objects are stored in the heap and the object gets "pointer" to the location in the heap. More on that in this video

The trick with the dependencies array in useEffect has to do with how equality works in JavaScript, a great article about this tell us that:

  1. If both operands are numbers and have the same value, they are strictly equal
  2. If both operands are strings and have the same value, they are strictly equal
  3. If both operands have reference to the same object or function, they are strictly equal

So if we have:

function App() {
  const num = 1;
  const arr = [1, 2, 3];
  const [state, setState] = React.useState(1);
  React.useEffect(() => {
    console.log("updated by change of num");
  }, [num]);

  React.useEffect(() => {
    console.log("updated by change of arr");
  }, [arr]);

  return (
    <button onClick={() => setState((prev) => prev + 1)}>
      increase {state}
    </button>
  );
}

The dependency array doesn't care if num has the same location in memory (which it doesn't due to being new in each function call) as the equality only checks for type and value, but it does care the place in memory for objects as for them to be equal they need to have the same reference.

Hope this is clear!