React中useRef与useState区别

140 阅读2分钟

一.定义变量时的区别

我在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>
   </>
)
  1. 如果快速点击修改useState创造出来的值,点击几次,定时器会执行几次,而且视图也会修改,因为使用setState修改值会重新加载组件,每个定时器形成闭包,所以会打印0,1,2div里面的值也会成为2
  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创造的值是没有闭包的,所以他一直会打印最新值