一.定义变量时的区别
我在5秒内,分别快速点击div2次会发生什么?
const [count, setCount] = useState(0)
let num=useRef(0)
setTimeout(() => {
//5秒后可以拿到5秒后的最新值
console.log(num, 'num')
//只会拿到执行定时器时作用域的值,也就是闭包
console.log(count, 'count')
}, 5000)
return (
<>
<div onClick={()=>setCount(count+1)}>{count}</div>
<div onClick={()=>num.current++}>{num.current}</div>
</>
)
- 如果快速点击修改
useState创造出来的值,点击几次,定时器会执行几次,而且视图也会修改,因为使用setState修改值会重新加载组件,每个定时器形成闭包,所以会打印0,1,2,div里面的值也会成为2。 - 如果快速点击修改
useRef创造出来的值,点击后视图不会发生改变,定时器中会打印一次,值为2,他不会形成闭包拿到的是最新值。
二.useRef可以拿到Dom元素
如果我先点击id为num再点击id为count会发生什么
let num=useRef(0)
const dom = useRef(null)
return (
<>
<div id='num' onClick={()=>num.current++}>{num.current}</div>
<div id='count' ref={dom} onClick={()=>dom.current.textContent =num.current }>嘿嘿</div>
</>
)
如问题一可知:修改useRef创造的值不会重新加载组件,所以我点击一次id为num的div,他的视图并不会改变,
但是我点击id为count的div时,'嘿嘿'变成了1,这是因为我点击id为count的div时,直接修改了他的dom元素中的值,并把这时已经变成了1的num赋值给他了,注意:这个时候组件依然没有重新加载,所以id为num的div其中的值依然是0
三.搭配使用
上面2个问题可得useState有闭包问题,useRef没有,useState可以重新渲染组件,但是useRef不行,他们搭配使用是不是可以把让他们互补勒?
const [count, setCount] = useState(0)
const num = useRef(count)
num.current = count
useEffect(() => {
setInterval(() => {
console.log(count, 'count')//无论点多少下,因为闭包,只会打印0
console.log(num, 'num')//点几下就是几,搭配使用解决了闭包
}, 2000)
}, [])
return (
<>
<div id='count' onClick={() => setCount(count + 1)}>{count}</div>
</>
)
从阿里的aHooks库中的useLatest源码可以得出结论是可以的,我上面代码可以看到,多次点击div后,定时器中count的值其实不会改变,因为他一直是获取的当前作用域的值也就是0(问题一证实),但是num的值一直在变,这是因为每次count被修改后会重新加载组件,然后count的值又会被重新赋值给num,所以num是最新值,而问题一可得useRef创造的值是没有闭包的,所以他一直会打印最新值