useRef
  # useRef原理
import React from "react";
import * as ReactDOMClient from "react-dom/client";
const AppDOM = React.createRef()
let root
let refIndex = 0
const render = () => {
  if(!root){
    root = ReactDOMClient.createRoot(AppDOM.current);
  }
  refIndex = 0
  root.render(<Children />);
}
const refs = []
const useRef = (initVal) => {
   if(!refs[refIndex]){
     refs[refIndex] = {current: initVal}
   }
   refIndex++
   return refs[refIndex - 1]
}
const Children = () => {
  const count1 = useRef(0)
  const count2 = useRef(0)
  const showCounts = () => {
    setTimeout(() => {
      alert(`count1:${count1.current}  count2:${count2.current}`)
    },5000)
  }
  return (
      <div>
        <div>
          count1值为:{count1.current}
          <button onClick={() => count1.current = ++count1.current}>
            改变count1
          </button>
        </div>
        <div>
          count2值为:{count2.current}
          <button onClick={() => count2.current = count2.current + 1}>
            改变count2
          </button>
        </div>
        <button onClick={() => render()}>
          重新render
        </button>
        <button onClick={showCounts}>
          5秒后输出count1、count2
        </button>
      </div>
  )
}
const App = () => {
  return (
      <div ref={AppDOM}>
        <Children/>
      </div>
  )
}
export default App
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 与useState区别
- useRef()是返回一个对象
{current: state},所以不管什么时候取useRef赋值的变量,都是最新的值。因为每次返回都是同一个对象的引用,每次修改count = count.current + 1都是改的这个对象的属性。 - 而useState每次调用返回的都是一个新的引用,所以就会造成下面这个问题:
 
import {useState} from "react";
const App = () => {
  const [count, setCount] = useState(0)
  const handleShowClick = () => {
    setTimeout(() => {
      alert(`count值为${count}`)
    },5000)
  }
  return (
      <div>
        <button onClick={() => setCount(count + 1)}>count加1</button>
        <button onClick={handleShowClick}>展示count</button>
      </div>
  )
}
export default App
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  点击【展示count】按钮后,5秒内多次点击【count加1】,最终展示效果为"count值为0"。
  这是因为每次render都会有一个作用域,点击【展示count】后,调用handleShowClick,此时这个方法体内拿到的count是0,可以理解为函数组件每次渲染都会形成一个闭包,闭包内有自己的作用域。所以5秒内不管改变多少次count值,最终展示效果都是"count为0"。
# 如何解决useState引起的闭包问题。
用useRef替换useState
import {useRef} from "react";
const App = () => {
  const count = useRef(0)
  const handleShowClick = () => {
    setTimeout(() => {
      alert(`count值为${count.current}`)
    },5000)
  }
  return (
      <div>
        <button onClick={() => count.current = count.current + 1}>count加1</button>
        <button onClick={handleShowClick}>展示count</button>
      </div>
  )
}
export default App
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 使用场景
- 存dom
 - 存值(任何时候都可以拿到最新值)
 
编辑  (opens new window)
  上次更新: 2023/02/16