Hooks 简化了 React 组件内部状态和副作用的管理。此外,可以将重复的逻辑提取到自定义 Hooks 中,以在整个应用程序中重复使用。Hooks 严重依赖于 JS 闭包。这就是为什么 Hooks 如此具有表现力和简单,但是闭包有时很棘手。5 w- @" F1 X" f% V
使用 Hooks 时可能遇到的一个问题就是过时的闭包,这可能很难解决。让我们从过时的装饰开始。然后,看看到过时的闭包如何影响 React Hooks,以及如何解决该问题。# N. n. n; a4 i M M+ D 1.过时的闭包 1 B( w( G$ L! Y 工厂函数 createIncrement(incBy) 返回一个increment和log函数的元组。调用时,increment()函数将内部value增加incBy,而log()仅打印一条消息,其中包含有关当前value的信息:/ z k2 {) m. e; f2 ^! L5 T
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 了。 ! r; G& l8 E/ a: c8 D- X1 B log()是一个过时的闭包。闭包 log()捕获了值为 "Current value is 0"的 message 变量。即使 value 变量在调用increment()时被增加多次,message变量也不会更新,并且总是保持一个过时的值 "Current value is 0"。过时的闭包捕获具有过时值的变量。6 v5 ?$ ]) g9 \ 2.修复过时的闭包 5 k1 e9 {1 X; r' C. J$ Y 修复过时的log()问题需要关闭实际更改的变量:value的闭包。我们将语句 const message = ...; 移动到 log() 函数内部:# J7 w S+ { ]$ h% |4 G: a
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"
现在,在调用了 3 次 increment() 函数之后,调用 log() 记录了实际value:"Current value is 3"。2 h$ `: t1 w; s d 3. Hooks 中的过时闭包. U. u* B& w6 ]1 Y K
3.1 useEffect() 6 {2 W- c, D `4 d. o 我们来看一下使用useEffect() 过时闭包的常见情况。在组件<WatchCount>中,useEffect() 中每2秒记录一次count的值:4 N9 H) T2 f1 w8 M9 {9 v" |
打开演示(https://codesandbox.io/s/use-state-fixed-zz78r)。再次快速单击按钮2次。计数器显示正确的值2。 * K; s. {5 Z" r9 g( K* v 当一个返回基于前一个状态的新状态的回调函数被提供给状态更新函数时,React确保将最新的状态值作为该回调函数的参数提供: : E8 y$ p( X3 S k2 ]0 t
这就是为什么在状态更新过程中出现的过时装饰问题可以通过函数这种方式来解决。- v7 O* r4 ~3 X1 l 4.总结 . d. s6 g$ b' H1 y当闭包捕获过时的变量时,就会发生过时的闭包问题。解决过时闭包的有效方法是正确设置React钩子的依赖项。或者,在失效状态的情况下,使用函数方式更新状态。5 S4 |) Y# Y% t