责任编辑早已过原作 Shadeed 许可译者。
Hooks 精简了 React 模块外部状况和过敏反应的管理工作。除此之外,能将多次重复的方法论抽取到自订 Hooks 中,以在整座插件中生物降解。
Hooks 轻微倚赖 JS 旋量群。这是为何 Hooks 这般具备感染力和单纯,但旋量群有时候很头疼。
采用 Hooks 时可能将碰到的两个难题是落伍的旋量群,这可能将极难化解。
让他们从落伍的点缀早已开始。接着,看一看到落伍的旋量群怎样负面影响 React Hooks,和怎样化解该难题。
1.落伍的旋量群
厂房表达式 createIncrement(incBy) 回到两个increment和log表达式的位元。初始化时,increment()表达式将外部value减少incBy,而log()仅列印两条最新消息,当中包涵相关现阶段value的重要信息:
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 了。
log()是两个落伍的旋量群。旋量群 log()捕获了值为 “Current value is 0”的 message变量。
即使 value 变量在初始化increment()时被减少多次,message变量也不会更新,并且总是保持两个落伍的值 “Current value is 0”。
落伍的旋量群捕获具备落伍值的变量。
2.修复落伍的旋量群
修复落伍的log()难题须要关闭实际更改的变量:value的旋量群。
他们将语句 const message = …; 移动到 log() 表达式外部:
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”。
3. Hooks 中的落伍旋量群
3.1 useEffect()
他们来看一下采用useEffect() 落伍旋量群的常见情况。
在模块<WatchCount>中,useEffect() 中每2秒记录一次count的值
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状况变量实际上早已减少了几次。
为何会这样?
第一次渲染时,状况变量count初始化为0。
模块安装后,useEffect()初始化 setInterval(log, 2000)计时器表达式,该计时器表达式计划每2秒初始化一次log()表达式。在这里,旋量群log()捕获到count变量为0。
之后,即使在单击Increase按钮时count减少,计时器表达式每2秒初始化一次的log(),采用count的值仍然是0。log()成为两个落伍的旋量群。
化解方案是让useEffect()知道旋量群log()倚赖count,并在count改变时正确处理间隔的重置
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> ); }正确设置依赖项后,一旦count发生变化,useEffect()就会更新旋量群。
3.2 useState()
<DelayedCount>模块有1个button ,以1秒延迟异步减少计数器。
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。
每次单击setTimeout(delay, 1000)将在1秒后执行delay()。delay()此时捕获到的 count 为 0。
两个delay()都将状况更新为相同的值:setCount(count + 1) = setCount(0 + 1) = setCount(1)。
这是因为第二次单击的delay()旋量群中已捕获了落伍的count变量为0。
为了化解这个难题,他们采用表达式式方法setCount(count => count + 1)来更新count状况
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。
当两个回到基于前两个状况的新状况的回调表达式被提供给状况更新表达式时,React确保将最新的状况值作为该回调表达式的参数提供
setCount(alwaysActualStateValue => newStateValue);这是为何在状况更新过程中出现的落伍点缀难题能通过表达式这种方式来化解。
4.总结
当旋量群捕获落伍的变量时,就会发生落伍的旋量群难题。
化解落伍旋量群的有效方法是正确设置React钩子的依赖项。或者,在失效状况的情况下,采用表达式方式更新状况。
~完,我是小智,我要去刷碗了。
原文:https://dmitripavlutin.com/react-hooks-stale-closures/