问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

二、在React中正确使用useState的姿势

发布网友 发布时间:2024-10-02 20:26

我来回答

1个回答

热心网友 时间:2024-10-19 11:32

??注意:因为我们不涉及ClassComponent,所有内容的都会以FunctionComponent形式进行

在上一节中,我们学习了如何创建一个React项目,并了解到了JSX语法的本质就是JavaScript。希望你有完成我留下来的小作业~

本节,我们来学习在React中如何使用useState来支持页面动态渲染,以及使用需要注意的一些点,怎样避免这些坑。

在React中,如果你想要使用一个支持可操作的变量,像下面?这样写是不行的

functionApp(){letnum=0//声明一个变量numconstadd=()=>{//add函数用于每次点击btn时将num+1num++console.log(num)}return(<divstyle={{margin:100}}><divstyle={{marginBottom:16}}>{num}</div><buttononClick={add}>加1</button></div>)}

我们在浏览器中打开

明明num被改变了,可为什么渲染的还是0呢?这还是因为,我们写的代码就是普通的JavaScript代码,上面的这段代码其实就等价于我们在原生JavaScript中这样写

letnum=0constel=document.getElmentById('root')el.innerHTML=numdocument.getElementById('btn').onclick=()=>{num++console.log(num)}

试问,我们没有在每次num++以后将这个值更新到el的innerHTML中,页面怎么会展示呢?

一、使用useState

我们使用着React,如果我们还需要每次num++以后来手动更新,那岂不是又回到了刀耕火种的时代了,所以React官方的APIuseState就出来了

useState是React上的一个方法,它接收一个默认值,并返回一个数组,数组包含两项,第一项是我们的数据,第二项是用来修改这个数据的函数。

functionApp(){const[num,setNum]=React.useState(0)//声明一个变量numconstadd=()=>{//add函数用于每次点击btn时将num+1setNum(num+1)console.log(num)}return(<div><div>{num}</div><buttononClick={add}>加1</button></div>)}

我们在浏览器中看一下效果

芜湖~,页面如我们预期的更新成功了???

二、多次使用同一个setState

我们有时可能会在一次事件中多次调用同一个setState

functionApp(){const[num,setNum]=React.useState(0)//声明一个变量numconstadd=()=>{setNum(num+1)console.log(num)setNum(num+2)console.log(num)}return(<div><div>{num}</div><buttononClick={add}>加1</button></div>)}

也许我们想要的效果是先将num+1,接着再将num+2这样的效果,但是很抱歉的告诉你,这是不行的,生效的只会是最后一个**setState**,页面上只会渲染每次加2的结果,所以这里需要注意。那可能有的大聪明会说,那我业务当中就是会有这样的需求啊,就是可能会经过多次设置呢。

好吧,的确是有这样的需要的,React中提供了另外一种写法,很简单?

constadd=()=>{setNum(n=>n+1)//一些操作setNumn(n=>n+2)}

这种写法是传入的就不是直接的值,而是一个function,它接受前面的state,我们可以根据这个stat来做一些处理以后,再返回一个state。所以我们这里调用完以后,页面上就会成功显示加3后的结果了。(但是一定要记住我说的:生效的只会是最后一个**setState**,如果你在最后仍写的是setNum(x),最后都会以x的值作为渲染)

三、使用useState存储对象类型数据

使用useState存储对象类型的数据应该是很多刚开始使用React遇到的坑点了,比如我~

我们有一个对象,存放的是小明的信息,希望在每次点击按钮时,小明都能长大一岁(小明:wdnmd???

functionApp(){const[user,setUser]=React.useState({name:'小明',age:18})//每次点击都将年龄+1constplus=()=>{user.age+=1setUser(user)console.log('点击打印',user)//每次点击打印}console.log('render打印',user)//每次render都会打印return(<divstyle={{margin:100}}><divstyle={{marginBottom:16}}>姓名:{user.name}</div><divstyle={{marginBottom:16}}>年龄:{user.age}</div><buttononClick={plus}>长大一岁</button></div>)}exportdefaultApp;

我们在浏览器中跑一下看下效果是否如我们所愿

我们发现,每次点击触发了,并且也更新了age,可是为什么没有触发render让页面更新呢?我们到源码中看一下怎么回事

constobjectIs=typeofObject.is==='function'?Object.is:is;functiondispatchSetState(fiber,queue,action){//省略......if(objectIs(eagerState,currentState)){//Fastpath.WecanbailoutwithoutschedulingReacttore-render.//It'sstillpossiblethatwe'llneedtorebasethisupdatelater,//ifthecomponentre-rendersforadifferentreasonandbythat//timethereducerhaschanged.return;}//省略......}

其中eagerState就是我们想要更新渲染的值,currentState就是当前页面上渲染的值,而objectIs就是判断这两个值是否相等,如果相等,则直接退出更新。

而我们这里两个值都是同一个引用,自然也就无法触发React的后续操作了,所以每次点击时数据都更新了,但是页面就是没有反应。

同时,这意味着基本类型如果更新前后的值都一致的话objectIs也会判定为true,导致页面不会更新(重新render)。知道了是什么原因,那么我们就知道怎么解决了,不就是新给一个对象嘛,我改就完了,于是一顿操作如下?

//省略......//每次点击都将年龄+1constplus=()=>{setUser({age:user.age+1})console.log('点击打印',user)//每次点击打印}console.log('render打印',user)//每次render都会打印//省略......

如果你是ts用户,并且开了代码检查,那肯定会发现报错了,提示你setUser中缺少参数name字段,如果你不是,页面会成功展示,并在你点击按钮时,会发现页面上的年龄虽然更新了,但是姓名不见了~

如果你是之前写Class方式的小伙伴,你肯定会觉得很疑惑。这是因为在hook中,每次修改的值都以传入的值来作为最后的值,已经不像之前写Class那般增量添加了~你必须这样写才可以保证字段不会被丢失?

//省略......//每次点击都将年龄+1constplus=()=>{setUser({...user,age:user.age+1})console.log('点击打印',user)//每次点击打印}console.log('render打印',user)//每次render都会打印//省略......

也就是我们需要做一个浅拷贝,同时将想要改变的值添加进去,才会达到我们想要的目的?

总结

下面我们进行技术总结:

useState接受一个初始值,它返回一个数组,里面包含两个值,第一个是拿来使用的值(state),第二个是用来更新这个值的函数(setState)

使用setState更新数据时,在它后面使用state还是未更新的值

如果想要setState依赖上一个setState的值,可以写一个函数,函数接收上一个state,并返回要设置的state,类似于setState(prevState=>newState)。

setState会将原本的值与传过来的值进行浅比较,如果前后两个值对比相等,则不进行渲染操作,直接return,所以对于Object类型的值在setState时需要进行一个浅拷贝操作。

希望能对你有所收获,如果你有兴趣从源码层面探索更多关于useState相关的知识,你可以看下一篇从源码层面来理解useState,不过这可能需要你具备一定的技术功底。当然你也可以选择粗略的过一下甚至直接跳过,这都不影响你学习React。(不过我还是建议你有精力的话可以粗略的过一下~)

原文:https://juejin.cn/post/7101860031804473381
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
一拳一拳打峰哥是什么歌_一拳一拳打峰哥歌曲介绍 网上办理这个准生证然后没有领取不知道点到哪了然后就没法再领取 绿油油的叠词是什么? 什么的小溪叠词填空 硅胶礼品特性 华东理工大学长江学院是一本吗 东华理工大学长江学院是公办还是民办大学 东华理工大学长江学院是一所... 模压硅胶制品如何成型的 东北理工大学长江学院是公办还是民办 东华理工大学长江学院是几本大学 我和老公都属狗,我是82年农历2月15的,老公是82年农历6月初6,想要属虎... 王移芝研究领域 ...老婆是1982.9.3(阴历7.16).请问我们要个属兔的孩子.几月比较... 寻仙中关于加属性点的问题 QQ寻仙人物在哪加属性点?从几级开始有属性点?技能点从哪来???为什么... 魔兽世界tbc战场荣誉点装备在哪换-tbc战场荣誉点装备兑换位置分享 计算机系列教材:大学计算机基础实验教程适用的出版社和发行年份是什么... 健身房的增肌训练计划是怎样的? 夏天开车如何保持车内空气清新求解答 为什么三体人不阻止古筝行动? ♾️能形容爱一个人吗? 《三体》中的"古筝计划"怎么没人逃生 关于开车开车窗有没什么讲究? 耳挂式耳机一定会漏音吗?有没有不漏音的?(三星手机能用的)求推荐,顺便... 我DNF驭剑士70级黑色瘟疫一套 手镯死亡抉择 项链龙灵石... 有个QQ好友把我删了我要看我们的聊天记录怎么办 三星RS552NRUAWW/SC冰箱的搁物板设计是否可调整? 古剑奇谭百里屠苏和师父谁更厉害 侠客风云传DLC天王归来新增养成事件时间表一览介绍_侠客风云传DLC天王... 北史家务乡沿革 我是1982年8月28日是农历我老婆是1981年10月6日请大师指点我的婚姻? 《tbc》荣誉装备在 我和老公都属狗,我是1982年正月初四生日,他是1982年农历2月24生日。属... 寻仙属性点、技能点怎么加 从原理上解读useState钩子函数 寻仙各个职业的属性点怎么加的? iphone6 plus和iphone6s plus有什么区别 寻仙怎么增加人物基本属性? 急求,合同制法警通过司法考试能不能转成法官,还有什么硬性的条件嘛... 杭州浙大博学教育咨询有限公司浙大博学核心团队 谁可以帮我找一份法警心得,谢谢了啊 做好自己的八字短句 苹果6s闪退黑屏,然后按下home键就会好了 现在豫p车可以在山西静乐下高速吗? 我关注的qq部落能不能设置不让任何人看到我关注的部落?不取消关注的... 怎么设置页码横版和竖版 怎么把小米手机国家设置成海外 特别吸粉的短句 永远奔赴最美好的自己! 郑州至项城、周口等地方的汽车怎么那么牛呀? 小米12 PRO 刷 MIUI 14 海外版(Android 12)