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"
[increment, log] = createIncrement(1)返回一个函数元组:一个函数增加内部值,另一个函数记录当前值。然后,increment()的3次调用将 value递增到3。最后,log()调用打印消息是 Current value is 0,这有点出乎意料的,因为此时 value 为 3 了。 3 e; n" t/ a; c) |) R& d/ H7 T, @7 h1 R log()是一个过时的闭包。闭包 log()捕获了值为 "Current value is 0"的 message 变量。即使 value 变量在调用increment()时被增加多次,message变量也不会更新,并且总是保持一个过时的值 "Current value is 0"。过时的闭包捕获具有过时值的变量。! Z/ V. k1 V$ x/ D( a 2.修复过时的闭包 . c. g/ V9 m. `. ]; E o6 Y+ M# Q 修复过时的log()问题需要关闭实际更改的变量:value的闭包。我们将语句 const message = ...; 移动到 log() 函数内部:& t* H$ i2 Y9 ~# w- q, }
现在打开演示(https://codesandbox.io/s/use-state-broken-0q994)。快速单击2次按钮。计数器仅更新为1,而不是预期的2。 + n. O2 t' |# ~' F# e# y: R. Y 每次单击setTimeout(delay, 1000)将在1秒后执行delay()。delay()此时捕获到的 count 为 0。两个delay()都将状态更新为相同的值:setCount(count + 1) = setCount(0 + 1) = setCount(1)。: c. ~" P" K. q7 d+ {3 z( E
这是因为第二次单击的delay()闭包中已捕获了过时的count变量为0。为了解决这个问题,我们使用函数式方法setCount(count => count + 1)来更新count状态: 5 j3 B) i k2 |; U s