译者:Dmitri Pavlutin
翻译者:后端小智
1. JS 中的旋量群
上面表述了两个厂房表达式 createIncrement(i),它回到两个increment表达式。后,每天初始化increment表达式时,外部计时器的值单厢减少i。
createIncrement(1) 回到两个存量表达式,该表达式表达式给inc表达式。当初始化inc()时,value 表达式加1。
第二次初始化inc()回到1,第三次初始化回到2,以此类推。
这挺趣的,如果初始化inc()还峭腹模块,JS 依然晓得现阶段 value 和 i 的存量,来看一看这玩意儿是怎样组织工作的。
基本原理就在 createIncrement() 中。当在表达式上回到两个表达式时,会有旋量群造成。旋量群捕捉语法结构回到值中的表达式 value 和 i。
语法结构回到值是表述旋量群的外部回到值。在此例中,increment() 的语法结构回到值是createIncrement()的回到值,当中包涵表达式 value 和 i。
不论在何方初始化 inc(),即使在 createIncrement() 的回到值以外,它都能出访 value 和 i。
旋量群是两个能从其语法结构作用域记住和修改表达式的表达式,不管执行回到值是甚么。
继续这个例子,能在任何地方初始化 inc(),即使在异步回调中也能:
2. React Hooks 中的旋量群
通过简化状态重用和副作用管理,Hooks 取代了基于类的组件。此外,咱们能将重复的逻辑提取到自表述 Hook 中,以便在应用程序之间重用。
Hooks 严重依赖于 JS 旋量群,但是旋量群有时很棘手。
当咱们使用两个有多种副作用和状态管理的 React 组件时,可能会遇到的两个问题是落伍的旋量群,这可能很难化解。
咱们从提炼出落伍的旋量群开始。然后,看一看落伍的旋量群怎样影响 React Hook,以及怎样化解这个问题。
3. 落伍的旋量群
厂房表达式createIncrement(i)回到两个increment表达式。increment 表达式对 value 减少i请输入代码 ,并回到两个记录现阶段 value 的表达式
在第二次初始化inc()时,回到的旋量群被分配给变量 log。对 inc() 的 3 次初始化的存量 value 为 3。
最后,初始化log() 打印 message “Current value is 1”,这是出乎意料的,因为此时 value 等于 3。
log()是落伍的旋量群。在第二次初始化 inc() 时,旋量群 log() 捕捉了具有 “Current value is 1” 的 message 表达式。而现在,当 value 已经是 3 时,message 表达式已经落伍了。
落伍的旋量群捕捉具有落伍值的表达式。
4.修复落伍旋量群的问题
使用新的旋量群
化解落伍旋量群的第一种方法是找到捕捉最新表达式的旋量群。
咱们找到捕捉了最新 message 表达式的旋量群。就是从最后一次初始化 inc() 回到的旋量群。
latestLog 捕捉的 message 表达式具有最新的的值 “Current value is 3”。
顺便说一下,这大概就是 React Hook 处理旋量群新鲜度的方式。
Hooks 实现假设在组件重新渲染之间,作为 Hook 回调提供的最新旋量群(例如 useEffect(callback)) 已经从组件的表达式回到值捕捉了最新的表达式。
关闭已更改的表达式
第二种方法是让logValue()直接使用 value。
让我们移动行 const message = …; 到 logValue() 表达式体中:
logValue() 关闭 createIncrementFixed() 回到值内的 value 表达式。log() 现在打印正确的消息“Current value is 3”。
5. Hook 中落伍的旋量群
useEffect()
现在来研究一下在使用 useEffect() Hook 时出现落伍旋量群的常见情况。
在组件 <WatchCount> 中,useEffect()每秒打印 count 的值。
打开 CodeSandbox 并单击几次加1按钮。然后看一看控制台,每2秒打印 Count is: 0。
咋这样呢?
在第二次渲染时,log() 中旋量群捕捉 count 表达式的值 0。过后,即使 count 减少,log()中使用的依然是初始化的值 0。log() 中的闭包是两个落伍的旋量群。
化解方案是让 useEffect()晓得 log() 中的旋量群依赖于count:
适当地设置依赖项后,一旦 count 更改,useEffect() 就更新旋量群。
同样打开修复的 codesandbox,单击几次加1按钮。然后看一看控制台,这次打印就是正确的值了。
正确管理 Hook 依赖关系是化解落伍旋量群问题的关键。推荐安装 eslint-plugin-react-hooks,它能帮助咱们检测被遗忘的依赖项。
useState()
组件<DelayedCount>有 2 个按钮:
点击按键 “Increase async” 在异步模式下以1秒的延迟递增计时器在同步模式下,点击按键 “Increase sync” 会立即减少计时器。现在打开 codesandbox 演示。点击 “Increase async” 按键然后立即点击 “Increase sync” 按钮,count 只更新到 1。
这是因为 delay() 是两个落伍的旋量群。
来看一看这个过程发生了甚么:
初始渲染:count 值为 0。点击 Increase async按钮。delay() 旋量群捕捉 count 的值 0。setTimeout() 1 秒后初始化 delay()。点击“Increase async”按键。handleClickSync() 初始化 setCount(0 + 1) 将 count 的值设置为 1,组件重新渲染。1 秒后,setTimeout() 执行 delay() 表达式。但是 delay() 中旋量群保存 count 的值是初始渲染的值 0,所以初始化 setState(0 + 1),结果count保持为 1。delay() 是两个落伍的旋量群,它使用在初始渲染期间捕捉的落伍的 count 表达式。
为了化解这个问题,能使用表达式方法来更新 count 状态:
现在 setCount(count => count + 1) 更新了 delay() 中的 count 状态。React 确保将最新状态值作为模块提供给更新状态表达式,落伍的旋量群的问题就化解了。
总结
旋量群是两个表达式,它从表述表达式的地方(或其语法结构范围)捕获表达式。旋量群是每个 JS 开发人员都应该晓得的两个重要概念。
当旋量群捕捉落伍的表达式时,就会出现落伍旋量群的问题。化解落伍旋量群的两个有效方法是正确设置 React Hook 的依赖项。或者,对于落伍的状态,使用表达式方式更新状态。
你认为旋量群使得 React Hook 很难理解吗?
原文:
https://dmitripavlutin.com/simple-explanation-of-javascript-closures/https://dmitripavlutin.com/react-hooks-stale-closures/