您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

我打破了React Hook必须是有序的,不能在条件语句中调用的枷锁。 {企业网站源码}

  • 时间:2022-06-29 00:39 编辑: 来源: 阅读:282
  • 扫一扫,手机访问
摘要:我打破了React Hook必须是有序的,不能在条件语句中调用的枷锁。 {企业网站源码}
React官网详细说明了Hook的一个限制:不要在循环、条件或者嵌套函数中调用Hook,并且确保总是在你的React函数的顶部,在任何返回之前调用。 遵循这个规则,你可以确保Hook在每次渲染中以相同的顺序被调用。 这使得React能够在重复调用useState和useEffect之间保持钩子状态正确 如果你对此感到好奇,我们将在下面进行更深入的解释。 )这种限制在开发过程中确实经常影响我们的开发体验。比如函数组件中的if语句提前返回,然后钩子调用出现,React推送的eslint规则也会给出警告。 函数App(){ if(XXX){ return null;} // ❌ React钩子“useState”被有条件调用。//在每个组件呈现中,必须以完全相同的顺序调用React挂钩。useState();Return 'Hello'}复制代码其实是很常见的用法。很多时候,当满足某个条件时,我们不希望组件继续渲染。 但是由于这个限制,我们只能将所有的钩子调用提升到函数的顶部,增加了额外的开销。 由于React的源代码过于复杂,接下来,本文将对Preact的源代码进行调试和讲解,Preact的原理类似,但要简单得多。 限制的原因这个限制不是React团队凭空造出来的,而是React Hook的实现设计真正逼出来的。 以PReact的Hook实现为例,它用数组和下标来找Hook (react用链表,但原理差不多)。 //当前运行的组件let currentComponent//当前hook的全局索引let current index//current index的第一次调用是0 usestate(' first ')//current index的第二次调用是1useState('second ')复制代码显示Hook的每次调用都对应一个全局索引。通过这个索引,到当前currentComponent上的_hooks数组,找到保存的值,也就是Hook返回的[state,useState]。然后,如果调用条件,比如第一个useState被调用的概率只有0.5://LetCurrent Component,当前运行的组件//LetCurrent Index,当前钩子的全局索引//第一次调用Current Index的时候是0IF(math . random()>:0.5){ useState(' first ')}//第二次调用currentIndex是1useState('second ')复制代码。Preact第一次渲染组件时,假设Math.random()返回的随机值为0.6,那么第一个钩子将被执行,此时组件上保存的_hooks状态为:_ hooks: [{value:' first ',update: function},{value:' second ',update: function},]复制代码用一个图来表示这个搜索过程如下:第一次渲染时假设第二次渲染时,Math.random()返回的随机值为0.3。此时只执行了第二个useState,所以它对应的全局currentIndex会是0。这时你从_hooks[0]得到的是第一个对应的状态,会造成渲染混乱。 第二次渲染就对了。本来应该是秒的值,却莫名其妙的被指向了第一。渲染完全错了!举个例子:导出默认函数app(){ if(math . random()>;0.5){ use state(10000)} const[value,setValue]= use state(0)return(& lt;div & gt& ltbutton onClick = {()= & gt;setValue(value+1)} & gt;+& lt;/button & gt;{ value } & lt/div & gt;)}复制代码的结果如下:混沌有办法破解限制吗?如果要解决全局索引递增带来的bug,那么可以考虑另一种存储钩子状态的方式。 如果不需要下标存储,是否可以考虑使用全局唯一的键来存储Hook,这样可以绕过下标带来的混乱?比如useState的API修改成这样:导出默认函数app(){ if(math . random()>;0.5) { useState(10000,' key 1 ');} const [value,setValue] = useState(0," key 2 ");return(& lt;div & gt& ltbutton onClick = {()= & gt;setValue(value+1)} & gt;+& lt;/button & gt;{ value } & lt/div & gt;);}复制代码。这样你用_hooks['key']查的话,前面的钩子就不会有什么意外的情况了。 也就是说,原来的存储方式是:_ hooks: [{value:' first ',update: function},{value:' second ',update: function},]复制代码修改后:_hooks: [key1: {value: 'first ',Update: function},key2: {value:' second ',update: function},]复制代码。注意数组本身支持对象的键值属性,不需要修改_hooks的结构。 转换源代码以尝试转换Preact源代码。其钩子包的位置在hooks/src/index.js下,找到useState方法:导出函数使用状态(初始状态){ current Hook = 1;返回useReducer(invokeOrReturn,initialState,undefined);}复制代码。它的底层调用useReducer,所以增加了一个新的key参数并传递:+导出函数使用状态(初始状态,key){ current hook = 1;+return useReducer(invokeOrReturn,initialState,undefined,key);}复制代码useReducer获取hook state://global index let current index export函数使用REDUCER (reducer,initial state,init){ const hook state = gethook state(current index++,2);胡克州。_reducer =减速器;返回hookState。_ value}将代码复制到兼容版本,有key时优先传入key值://global index let current index+export函数使用reducer (reducer,init,key){+const hook state = gethook state(key | | current index++,2);胡克州。_reducer =减速器;返回hookState。_ value}复制代码,最后转换getHookState方法:function getHookstate (index,type){ const hooks = current component。_ _ hooks || (currentcomponent。_ _ hooks = {_ list: [],_ pending effects: [],});//输入键值可以是字符串或符号+if (typeof index!== 'number') {+ if(!钩子。_list[index]) {+ hooks。_ list[index]= { };+}+} else { if(index & gt;=钩子。_list.length) { hooks。_ list . push({ });} }//这里自然支持访问键值的方式。回钩。_ list[index];}复制代码是在这里设计的。当键值传入时,新的状态在初始化时不会被推入数组,但可以通过下标直接写入。获得状态挂钩的原始编写方法。_list[index]本身支持通过键从数组中获取值,无需任何更改。 至此,转换完成。 尝试新用法:导出默认函数app(){ if(math . random()>;0.5) { useState(10000,' key 1 ');} const [value,setValue] = useState(0,' key 2 ');return(& lt;div & gt& ltbutton onClick = {()= & gt;setValue(value+1)} & gt;+& lt;/button & gt;{ value } & lt/div & gt;);}复制代码ok自动编译。事实上,React团队也考虑过为每个调用添加一个键值的设计。为什么Dan Abramov中的顺序调用对React Hooks很重要?该建议已在中详细解释 多个缺陷导致这个提议被拒绝,尤其是当你自己设置钩子的时候。例如,您提取了一个useforminput:const value key = symbol();函数useFormInput() { const [value,setValue]= useState(value key);return { value,onChange(e){ setValue(e . target . value);}, };}复制代码,在组件中反复调用:函数Form(){//Use Symbol const name = useFormInput();//再次使用了相同的符号const surname = useforminput();// ...return(& lt;& gt& lt输入{...name }/& gt;& lt输入{...姓氏}/& gt;{/* ...*/} & lt;/& gt;)}复制代码。这时候这种通过key寻找钩子状态的方式就会冲突。 但我的想法是,是否可以借助巴别塔插件的编译能力,在编译时自动给每个钩子调用注入一个键。伪代码如下:traverse(node){ if(isreatheokcalling(node)){ add function parameter(node,Getuniqkey (node)}}复制代码生成这样的代码:function form(){+constname = useforminput(' key _ 1 ');+const surname = useFormInput(' key _ 2 ');// ...return(& lt;& gt& lt输入{...name }/& gt;& lt输入{...姓氏}/& gt;{/* ...*/} & lt;/& gt;)}+function useFormInput(key){+const[value,setValue]= use state(key);return { value,onChange(e){ setValue(e . target . value);}, };}复制码密钥的生成策略可以是随机值,也可以是注入一个符号。没关系,只要运行时不会变。 可能有几个地方我没有仔细考虑。对此有想法的同学欢迎加我微信sshsunlight讨论。当然,单纯交朋友也没问题。新都欢迎大佬或者人。 本文的摘要只是一篇探究性文章:详细介绍钩子实现的一般原理和限制。探索修改源代码机制绕过限制的方法,其实是为了帮助大家更好的理解Hook。 我不想取消这些限制。我觉得也是一种设计选择。 如果Hook可以在任何一个子函数和任何一个条件表达式中调用,那么代码会变得更加难以理解和维护。 如果你真的想更灵活地使用类似的钩子能力,Vue3的底层响应式集合依赖的原理可以完美地绕过这些限制,但是更加灵活必然会增加更多的维护风险。 谢谢你。我是宋承宪。我目前在字节跳动的Web Infra团队工作。目前团队还缺人(尤其是北京) 为此我建立了一个氛围特别好的招聘社区,大家可以在这里尽情讨论面试相关的想法和问题。也欢迎你加入进来,随时把简历发给我。 了解更多,加入我们的前台学习圈。


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【技术支持|常见问题】1502企业站群-多域名跳转-多模板切换(2024-04-09 12:19)
【技术支持|常见问题】1126完美滑屏版视频只能显示10个(2024-03-29 13:37)
【技术支持|常见问题】响应式自适应代码(2024-03-24 14:23)
【技术支持|常见问题】1126完美滑屏版百度未授权使用地图api怎么办(2024-03-15 07:21)
【技术支持|常见问题】如何集成阿里通信短信接口(2024-02-19 21:48)
【技术支持|常见问题】算命网微信支付宝产品名称年份在哪修改?风水姻缘合婚配对_公司起名占卜八字算命算财运查吉凶源码(2024-01-07 12:27)
【域名/主机/服务器|】帝国CMS安装(2023-08-20 11:31)
【技术支持|常见问题】通过HTTPs测试Mozilla DNS {免费源码}(2022-11-04 10:37)
【技术支持|常见问题】别告诉我你没看过邰方这两则有思想的创意广告! (2022-11-04 10:37)
【技术支持|常见问题】你正确使用https了吗? [php源码](2022-11-04 10:37)

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部