携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情
1.问题1
如下,我们点击修改num的按钮3次,请问控制台输出什么?
import {useEffect,useState} from "react"
export default function Home() {
const [num,setNum] = useState(1)
useEffect(()=>{
setInterval(()=>{
console.log(num)
},1000)
},[])
//修改num
const addNum = () => {
setNum(num+1)
}
return <div>
<div>{num}</div>
<div onClick={addNum}>修改num</div>
</div>
}
粗心大意的我第一次看就自信的回答控制台输出1,2,3。这就上了面试官的大当了。这是一道十分经典的hooks陷阱问题。
hooks陷阱
react数据发生改变后,函数组件会重新执行一遍。此时这里面就有闭包陷阱问题,react每次更新都是一份独立的函数组件,学过闭包的都清楚,函数里面的函数返回所依赖的变量会被延迟释放,因此每次组件更新后,每份组件都对自己的数据保持单独的引用。
题目解答
- 首先useEffect依赖是空数组,我们知道定时器只会在组件初始化后执行一次,组件更新不会产生新的定时器。
- 当页面挂载完成后,此时定时器依赖的num是1。然后我们点击修改num三次,虽然组件更新了,但是定时器一直是第一次的,它引用的变量也一直是1。所以无论我们点击多少次修改Num.控制台输出一直是1
页面效果
2.问题2
如下,点击修改Num后,请问控制台输出什么?页面最终显示什么?
import {useState} from "react"
export default function Home() {
const [num,setNum] = useState(1)
const addNum = () => {
setNum(num+1)
setNum(num+2)
console.log(num)
setNum(num+3)
setNum(num+4)
console.log(num)
}
return <div>
<div>{num}</div>
<div onClick={addNum}>修改num</div>
</div>
}
这个题不清楚useState的数据更新可能就给出各种答案,首先我们聊聊useState的核心
usetate特点
- 异步
useState的更新是异步的
- 合并更新
useState多次对同一份数据更新时,会出现数据合并现象,这个现象vue也是存在。并且会取批量更新的最后一个去更新数据
- 特殊性
useState存在一个特殊机制可以破解合并更新,在useState修改数据时传递一个回调函数即可
题目解答
首先开始页面挂载后,页面显示的是1。当点击修改num后,根据事件循环队列机制,首选执行同步代码,控制台输出1,1.然后多份useState对一个数据进行更新,因此产生更新合并,选择最后的数据作为更新因此num+4 最终页面会显示5
页面效果
3.问题3
如下,点击修改num,请问控制台输出什么?页面最终显示什么?
import {useEffect,useState} from "react"
export default function Home() {
const [num,setNum] = useState(1)
const addNum = () => {
setNum(num+1)
setNum(num+2)
console.log(num)
setNum(num=>num+1)
setNum(num=>num+1)
console.log(num)
}
return <div>
<div>{num}</div>
<div onClick={addNum}>修改num</div>
</div>
}
问题解答
这个是对问题2的拓展,其实问题2已经给出了useState的特点,首次页面显示1.当我们点击修改按钮后,首先执行同步代码,控制台输出1,1。然后自己看上面对useState的特点总结,其中两份setNum会产生合并,因此num+2。但是下面的setState两份传入的是回调函数,不会对批量更新进行合并,二者都会执行。因此最终num+4,页面显示5
页面效果
4.问题4
使用useState修改数据后,如何获取最新修改的数据?
方案一:setState传入回调函数
setState的参数其实是可以传递一个回调函数,回调函数的参数是更新前的数据,我们在执行修改后可以直接获取其最新数据。
import {useState} from "react";
export default () => {
const [age,setAge] = useState(0);
return (
<div>
<div onClick={()=>setAge((age)=>{
age = age+1;
console.log("最新的age",age)//1
return age
})}>修改age
</div>
</div>
)
}
方案二:useEffect
useEffect监听数据,当数据更新后会自动触发useEffect的回调函数,由于useEffect的回调函数执行时,最新的数据已经挂载到dom上,因此我们可以获取最新的数据。
import {useState,useEffect} from "react";
export default () => {
const [age,setAge] = useState(0);
useEffect(()=>{
console.log("最新的age",age);
},[age])
return (
<div>
<div onClick={()=>setAge(age+1)}>
修改age
</div>
</div>
)
}
总结
希望大家点赞支持,没懂的请大家评论区见