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()
}
}注意事项
- 不要在渲染期间读写 ref.current(除了初始化),应在事件处理函数或 effect 中操作
- ref.current 的修改不会触发重渲染,所以渲染依赖的数据不能用 ref 存储