当前位置: 首页 > Web前端 > vue.js

使用ReactHooks时,需要注意过时的闭包!

时间:2023-03-31 20:51:34 vue.js

作者:Shadeed译者:FrontendXiaozhi来源:dmitripavlutin有梦想,有干货,微信搜索【大千世界】关注这个洗碗盘的清晨智慧。本文已收录到GitHubhttps://github.com/qq449245884/xiaozhi,里面有完整的测试站点、资料和我的一线厂商访谈系列文章。Hooks简化了React组件的内部状态和副作用的管理。此外,可以将重复的逻辑提取到自定义Hook中,以便在整个应用程序中重复使用。Hooks严重依赖JS闭包。这就是为什么Hooks如此富有表现力和简单,而Closures有时却很棘手。使用Hooks时可能会遇到的一个问题是陈旧的闭包,这可能很难解决。让我们从过时的装饰开始。然后,看看过时的闭包如何影响ReactHooks,以及如何修复它。1.过时的闭包工厂函数createIncrement(incBy)返回增量和日志函数的元组。调用时,increment()函数将内部值递增incBy,而log()只是打印一条消息,其中包含有关当前值的信息:functioncreateIncrement(incBy){letvalue=0;函数增量(){值+=incBy;控制台日志(值);}constmessage=`当前值为${value}`;函数日志(){console.log(消息);}返回[增量,日志];}const[增量,日志]=createIncrement(1);增量();//1增量();//2增量();//3//不能正常工作!日志();//"Currentvalueis0"[increment,log]=createIncrement(1)返回一个函数元组:一个函数增加内部值,另一个函数记录当前值。然后,3次调用increment()将值递增到3。最后,log()调用打印消息当前值为0,这有点出乎意料,因为现在值是3。log()是一个过时的闭包。闭包log()捕获值为“当前值为0”的消息变量。即使在调用increment()时value变量被递增多次,message变量也不会更新并且始终持有一个过时的值“当前值为0”。过时的闭包捕获具有过时值的变量。2.修复过时的闭包要修复过时的log()问题,需要关闭实际改变的变量:value的闭包。我们移动语句constmessage=...;在log()函数内部:functioncreateIncrement(incBy){letvalue=0;函数增量(){值+=incBy;控制台日志(值);}functionlog(){constmessage=`当前值为${value}`;控制台日志(消息);}return[increment,log];}const[increment,log]=createIncrement(1);increment();//1增量();//2增量();//3//有效!日志();//"Currentvalueis3"现在调用increment()函数3次后,调用log()记录实际值:"Currentvalueis3"。3.Hooks中过时的闭包3.1useEffect()让我们看一下使用useEffect()过时闭包的常见情况。在组件中,useEffect()每2秒记录一次count的值functionWatchCount(){const[count,setCount]=useState(0);useEffect(function(){setInterval(functionlog(){console.log(`Countis:${count}`);},2000);},[]);return(

{count}setCount(count+1)}>增加
);}打开示例(https://codesandbox.io/s/stale-closure-use-effect-broken-2-gyhzk)然后点击增加按钮几次。然后查看控制台,Countis:0每2秒出现一次,尽管count状态变量实际上已经递增了几次。为什么?在第一次渲染时,状态变量count被初始化为0。组件挂载后,useEffect()调用setInterval(log,2000)定时器函数,该函数被安排为每2秒调用log()函数。在这里,闭包log()捕获了count变量为0。之后,即使单击Increase按钮??时计数增加,定时器函数每2秒调用一次log(),使用count的值仍然为0.log()成为过时的闭包。解决方法是让useEffect()知道闭包log()依赖于count,正确处理count变化时间隔的重置functionWatchCount(){const[count,setCount]=useState(0);useEffect(function(){constid=setInterval(functionlog(){console.log(`Countis:${count}`);},2000);returnfunction(){clearInterval(id);}},[数数]);return(
{count}setCount(count+1)}>Increase
);}正确设置依赖后,一旦计数发生变化,useEffect()闭包会更新。3.2useState()组件有1个按钮,它以1秒的延迟异步增加计数器。functionDelayedCount(){const[count,setCount]=useState(0);functionhandleClickAsync(){setTimeout(functiondelay(){setCount(count+1);},1000);}return(
{count}Increaseasync
);}现在打开演示(https://codesandbox.io/s/use-....2quickbuttonclicks.CounteronlyUpdateto1insteadoftheexpected2.每次点击setTimeout(delay,1000)都会在1秒后执行delay(),此时delay()捕获的计数为0。两个delay()都将状态更新为相同的值:setCount(count+1)=setCount(0+1)=setCount(1)。这是因为在第二次点击的delay()闭包中捕获了0的过时计数变量。为了解决这个问题,我们使用函数方法setCount(count=>count+1)来更新计数状态functionDelayedCount(){const[count,setCount]=useState(0);functionhandleClickAsync(){setTimeout(functiondelay(){setCount(count=>count+1);},1000);}函数handleClickSync(){setCount(count+1);}return(
{count}增加异步增加同步
);}打开演示(https://codesandbox.io/s/use-....再次快速点击按钮2次计数器显示正确的值为2。函数,React确保最新的状态值作为参数提供给回调函数setCount(alwaysActualStateValue=>newStateValue);这就是为什么在状态更新过程中出现的过时装饰问题可以通过该函数解决的原因。4.总结当闭包捕获到过时变量时,就会出现过时闭包问题,解决过时闭包的有效方法是正确设置Reacthook的依赖。或者,在无效状态的情况下,使用function方法来更新状态~最后我是小智,我要走了g洗碗。代码部署后可能存在的bug,无法实时获知。事后为了解决这些bug,花费了大量的时间在日志调试上。顺便推荐一个好用的bug监控工具Fundebug。原文:https://dmitripavlutin.com/re...交流有梦想,有干货,微信搜索【走向世界的大招】关注这位凌晨还在洗碗的洗碗智慧。本文GitHubhttps://github.com/qq44924588...已收录,有完整的测试站点、资料和我的一线厂商访谈系列文章。