Hooks 简化了 React 组件内部状态和副作用的管理。此外,可以将重复的逻辑提取到自定义 Hooks 中,以在整个应用程序中重复使用。Hooks 严重依赖于 JS 闭包。这就是为什么 Hooks 如此具有表现力和简单,但是闭包有时很棘手。( y  Y/ ]( [( R: e; x5 p 5 f5 d. i; o4 X  @6 |$ `. F& y0 } 1.过时的闭包 7 x3 {. L* z6 p6 B        工厂函数 createIncrement(incBy) 返回一个increment和log函数的元组。调用时,increment()函数将内部value增加incBy,而log()仅打印一条消息,其中包含有关当前value的信息:- U- u* x* s1 y' m9 z function createIncrement(incBy) {
  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(); //  1
increment(); //  2
increment(); //  3
// 不能正确工作!
log();       //  "Current value is 0"/ I, [( D. T/ v" G3 L        log()是一个过时的闭包。闭包 log()捕获了值为 "Current value is 0"的 message 变量。即使 value 变量在调用increment()时被增加多次,message变量也不会更新,并且总是保持一个过时的值 "Current value is 0"。过时的闭包捕获具有过时值的变量。/ q. Y7 E0 }. M& ?# T/ G! R 2.修复过时的闭包 4 q- s- u, u6 |# H# M9 T6 Z        修复过时的log()问题需要关闭实际更改的变量:value的闭包。我们将语句 const message = ...; 移动到 log() 函数内部:2 j( a) f) I" I, P" D 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(); //  1
increment(); //  2
increment(); //  3
// Works!
log();       // "Current value is 3"% ^8 z* ?+ B, Z2 L# W 3. Hooks 中的过时闭包   D3 d# Z6 U( V" g# x 3.1 useEffect() . N/ |; `7 T7 H. J2 w5 [4 t- s& h        我们来看一下使用useEffect() 过时闭包的常见情况。在组件<WatchCount>中,useEffect() 中每2秒记录一次count的值:) T# r/ v9 \( x6 r. P3 c: _ 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>
  );
}https://codesandbox.io/s/stale-closure-use-effect-broken-2-gyhzk )并点击几次增加按钮。然后看看控制台,每2秒出现一次Count is: 0,尽管count状态变量实际上已经增加了几次。0 Q+ \- R6 K" g/ ~, E/ J 
 
$ \7 c+ T0 F" C/ c% \! a! N, ~ ( ]6 L) G- i. }$ N( H " [" h6 g2 V* W% f8 b. q1 q 5 S4 P7 E7 z  p3 w. x" L. O 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>
  );
}4 x$ M  E- G5 v' q 3.2 useState() + N2 g. }" j5 n0 ~% F0 h: J. k   N7 t  W, |  \ 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>
  );
}https://codesandbox.io/s/use-state-broken-0q994 )。快速单击2次按钮。计数器仅更新为1,而不是预期的2。; U! ^+ S# z6 s0 }. H0 U8 Q        每次单击setTimeout(delay, 1000)将在1秒后执行delay()。delay()此时捕获到的 count 为 0。两个delay()都将状态更新为相同的值:setCount(count + 1) = setCount(0 + 1) = setCount(1)。+ E8 H% O: ?* `$ m9 |& B2 D$ ^: H1 P + d2 b5 g3 C. C. M  m 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>
  );
}https://codesandbox.io/s/use-state-fixed-zz78r )。再次快速单击按钮2次。计数器显示正确的值2。. ]4 r. u" m6 x9 } - p6 M! M+ v$ v1 X5 V) i setCount(alwaysActualStateValue => newStateValue);% P& m+ x. j4 }: t# n 4.总结 " O! |5 }+ ~) u+ o$ I. I 当闭包捕获过时的变量时,就会发生过时的闭包问题。解决过时闭包的有效方法是正确设置React钩子的依赖项。或者,在失效状态的情况下,使用函数方式更新状态。; O. W( b" u1 D; Z* V , X" u9 p+ S0 ~