useRef 和 useState 的区别?useRef 有哪些使用场景?

核心区别

特性 useState useRef
修改是否触发重渲染 ✅ ❌
值的持久化 ✅ ✅
读取方式 通过 state 变量 .current 属性
适用场景 需要渲染的数据 不需要渲染的数据
// useState:修改触发重渲染
const [count, setCount] = useState(0)
setCount(1) // 触发重渲染

// useRef:修改不触发重渲染
const timerRef = useRef(null)
timerRef.current = setInterval(...) // 不触发重渲染

useRef 使用场景

1. 访问 DOM 元素

function Input() {
  const inputRef = useRef(null)

  useEffect(() => {
    inputRef.current.focus()
  }, [])

  return <input ref={inputRef} />
}

2. 存储定时器/订阅的 ID

function Timer() {
  const timerRef = useRef(null)
  const [seconds, setSeconds] = useState(0)

  useEffect(() => {
    timerRef.current = setInterval(() => {
      setSeconds(s => s + 1)
    }, 1000)
    return () => clearInterval(timerRef.current)
  }, [])

  const stop = () => clearInterval(timerRef.current)

  return <div>{seconds} <button onClick={stop}>Stop</button></div>
}

3. 保存最新值(避免闭包陷阱)

function Counter() {
  const [count, setCount] = useState(0)
  const countRef = useRef(count)

  // 保持 ref 始终是最新的
  useEffect(() => {
    countRef.current = count
  }, [count])

  useEffect(() => {
    const timer = setInterval(() => {
      // 读取最新值,不会有闭包问题
      console.log(countRef.current)
    }, 1000)
    return () => clearInterval(timer)
  }, [])

  return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}

4. 标记组件是否首次渲染

function Component() {
  const isFirstRender = useRef(true)

  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false
      return // 首次渲染不执行
    }
    // 后续依赖变化时执行
    doSomething()
  }, [dep])
}

5. 保存可变值且不触发渲染

function Map() {
  const mapInstanceRef = useRef(null)

  useEffect(() => {
    mapInstanceRef.current = new MapSDK(container)
  }, [])

  const zoomIn = () => {
    mapInstanceRef.current.zoomIn()
  }
}

注意事项

同分类其他题目