This is the 7th day of my participation in the August Text Challenge.More challenges in August

1. First the cliche closure

function createIncrement(val) {
  let value = 0;

  function increment() {
    value += incBy;
    console.log(value);
  }

  const message = `Current value is ${value}`;
  function log() {
    console.log(message);
  }
  
  return [increment, log];
}

const [increment, log] = createIncrement(1);
increment(); // logs 1
increment(); // logs 2
increment(); // logs 3
// Does not work!
log();       // logs "Current value is 0"
Copy the code

[increment, log] = createIncrement(1) Returns two functions, one is to increment the value, the other is to print the current value. When increment is invoked three times, the value of value is 3. The final message printed by log() is “current value is 0”, which is not consistent with the expected value of 3. Log () is a closure that captures the message variable. The message variable is not kept updated despite multiple calls to INCREMENT vulue.

2 Fix closures

Fix log to print updated value, move message=… ; Inside the log function

function createIncrement(incBy) {
  let value = 0;

  function increment() {
    value += incBy;
    console.log(value);
  }

  function log() {
    const message = `Current value is ${value}`;
    console.log(message);
  }
  
  return [increment, log];
}

const [increment, log] = createIncrement(1);
increment(); // logs 1
increment(); // logs 2
increment(); // logs 3
// Works!
log();       // logs "Current value is 3"
Copy the code

Now the value of log is 3.

\

Closure in 3 hooks

3.1 useEffect ()

function WatchCount() { const [count, setCount] = useState(0); useEffect(function() { setInterval(function log() { console.log(`Count is: ${count}`); }, 2000); } []); return ( <div> {count} <button onClick={() => setCount(count + 1) }> Increase </button> </div> ); }Copy the code

After clicking the button multiple times, the console prints Count is 0, which has actually been incremented multiple times. Why is that? After the component is mounted, useEffect calls setInterval(log, 2000) to print every 2 seconds, and the count variable captured by the closure log is 0. Even if count is increased multiple times, the log closure still uses the initial rendered value of count=0. We know that useEffect’s closure log depends on the count variable

function WatchCount() {
  const [count, setCount] = useState(0);

  useEffect(function() {
    const id = setInterval(function log() {
      console.log(`Count is: ${count}`);
    }, 2000);
    return function() {
      clearInterval(id);
    }
  }, [count]);

  return (
    <div>
      {count}
      <button onClick={() => setCount(count + 1) }>
        Increase
      </button>
    </div>
  );
}
Copy the code

Properly set dependencies, useEffect updates the closure as count changes

3.2 useState

function DelayedCount() {
  const [count, setCount] = useState(0);

  function handleClickAsync() {
    setTimeout(function delay() {
      setCount(count + 1);
    }, 1000);
  }

  return (
    <div>
      {count}
      <button onClick={handleClickAsync}>Increase async</button>
    </div>
  );
}
Copy the code

Click button twice quickly and count is still 1, not 2. Delay 1 second after each click to call delay function. Delay function capture count is always 0. SetCount (count + 1) = setCount(0 + 1) = setCount(1) 0 To fix this problem, we update count with the function setCount(count => count + 1)

function DelayedCount() {
  const [count, setCount] = useState(0);

  function handleClickAsync() {
    setTimeout(function delay() {
      setCount(count => count + 1);
    }, 1000);
  }

  function handleClickSync() {
    setCount(count + 1);
  }

  return (
    <div>
      {count}
      <button onClick={handleClickAsync}>Increase async</button>
      <button onClick={handleClickSync}>Increase sync</button>
    </div>
  );
}
Copy the code

The callback function returns a new state based on the previous state.

4 summarizes

Closures often catch a variable that is updated. An effective way to fix closures is to set the correct dependencies in React hooks, or update the state in a function