Skip to content Skip to sidebar Skip to footer

How To Create A Stale Closure Like The One In React's Useeffect Hook Without Using The Actual Useeffect Hook?

I know how closures work but I'm failed to understand how a stale closure gets create in a React's useEffect in the absence of an exhaustive dependencies array. To do so, I'm tryin

Solution 1:

What am I doing wrong? What should I do to create a stale closure like the one we get in React's useEffect when we don't provide the complete dependencies array?

You don't get a stale closure because you call the createIncrement function only once.

In order to re-render a functional react component, react calls the component function again. This creates a new scope which has no link to the scope created in the previous call to the function component.

To get a stale closure, you need to call createIncrement more than once.

functioncreateIncrement(incBy, functionCallIdentifier, intervalDelay) {
  let value = 0;

  functionincrement() {
    value += incBy;
    console.log("inside call: " + functionCallIdentifier + " - value: " + value);
  }

  increment();
  
  functionuseEffect(fun) {
    fun();
  }

  useEffect(function () {
    setInterval(functionlog() {
      console.log("inside call: " + functionCallIdentifier + " - count: " + value);
    }, intervalDelay);
  });
}

createIncrement(1, "first call", 2000);

// calling again but 'setInterval' set in the first call will continue// to log the latest value of variable "value" that it closed over, i.e. 1 createIncrement(2, "second call", 4000);

Why does a stale closure is created when we don't give exhaustive dependencies in a useEffect? Why doesn't the code in a useEffect hook's callback just use the lexical scope, like a normal function would, and print the actual value?

The above code example should give you an idea of why the stale closure gets created. useEffect's callback function indeed uses the lexical scope but the difference is that calling the function component again creates a new scope but the old callback function of the useEffect hook set in the previous render of the component will continue to see the values from the scope in which it was created.

This behavior is not specific to react - this is how every function in javascript behaves: each function closes over the scope in which it is created.

Until the previous callback is cleared before setting the new callback in the useEffect hook, old callback will continue to log the values from the scope that it closed over.

Following code example uses a clean up function to clear the interval set in the first call to the createIncrement function.

functioncreateIncrement(incBy, functionCallIdentifier, useEffectCleanupFn) {
  if (useEffectCleanupFn) {
    useEffectCleanupFn();
  }

  let value = 0;

  functionincrement() {
    value += incBy;
    console.log("inside call: " + functionCallIdentifier + " - value: " + value);
  }

  increment();
  
  let cleanupFn;

  functionuseEffect(fun) {
    cleanupFn = fun();
  }

  useEffect(function () {
    let id = setInterval(functionlog() {
      console.log("inside call: " + functionCallIdentifier + " - count: " + value);
    }, 2000);

    return() =>clearInterval(id);
  });

  return cleanupFn;
}

let cleanupFn1 = createIncrement(1, "first call");

setTimeout(() => {
  createIncrement(2, "second call", cleanupFn1);
}, 4000);

Post a Comment for "How To Create A Stale Closure Like The One In React's Useeffect Hook Without Using The Actual Useeffect Hook?"