当前位置: 首页 > Web前端 > JavaScript

使用Hooks很容易出错

时间:2023-03-27 11:12:24 JavaScript

昨天有朋友给我发了一个demo,我来说说原因。看到了,好人,一个小demo,知识点包括:Hooks的闭包问题的状态是如何组装的,相信看完这个demo,你会对函数组件有更深的理解。令人困惑的DemoDemo包含一个按钮和一个列表。Add{list.map(val=>val)}

点击按钮,调用add方法,插入item列表:让i=0;导出默认函数App(){const[list,setList]=useState([]);constadd=()=>{//...};return(Add{list.map(val=>val)}
);}显示效果:烧脑的部分是通过调用add方法插入的是单击,将调用add方法的按钮:constadd=()=>{setList(list.concat({i++}));};点击添加按钮7次最终显示效果:那么问题来了,点击带有数字的按钮后会有什么效果(会调用和点击添加按钮一样的add方法)?状态组装和关闭问题如果您认为将插入一个新按钮:那您就错了。正确答案是:点击对应的按钮后,列表长度变成按钮对应的数字+1,最后一项的数字为点击前的最大数字+1。比如点击前最大数为6,如果点击0,则列表长度变为0+1=1,最后一项为6+1=7:如果点击2,则列表长度变为2+1=3,而最后一项是6+1=7:这是两个因素共同作用的结果:Hoo`ks`的闭包问题和state是如何组装的分析我们来看再次在add方法中:constadd=()=>{setList(list.concat({i++}));};点击按钮后会调用add,所以会根据add所属的上下文(App函数)形成一个闭包。其中:addlistsetListi属于模块级作用域,不在list和setList来自useState调用后的返回值的闭包中:const[list,setList]=useState([]);一个常见的认知误区是:多次调用useState返回的列表是同一个引用。事实上,每次调用useState返回的list都是根据下面的公式计算出来的:basestate+update1+update2+...=currentstate,所以是一个全新的对象。首屏渲染时:App组件的第一次render创建list=[]Add依赖add形成一个闭包,闭包中的list=[]next,点击Add按钮:调用add方法,该方法来自首屏渲染创建的闭包。add方法中的list依赖来自同一个闭包,所以list=[]{i++}/button>依赖add形成一个闭包。闭包中的列表=[]所以,对于按钮0,任何时候它被点击,它实际上执行:setList([].concat({i++}));那么如何解决这个问题也很简单,把setList的参数改成函数形式://beforesetList(list.concat({i++}));//在setList(list=>list.concat({i++}));函数参数中的列表来自Hooks中保存的列表,而不是闭包中的列表。总结由于Hooks总是在渲染组件时计算新的状态,这给Hooks带来了沉重的精神负担。相比之下,用细粒度更新实现的Hooks(比如VUE的CompositionAPI)可以实时更新状态,操作更直观。