React(二): 避免副作用函数反复创建的小技巧
在 React 中, 使用 useEffect
注册事件或副作用函数时, 如果依赖发生变化, 就会导致副作用函数被反复销毁与重建. 在一些场景中, 可以通过 useRef
将副作用函数的引用保持不变, 从而减少不必要的注册/解绑开销.
示例对比
普通方式:函数随着 state 变化不断注册
import { useEffect, useState } from "react"
function ComponentA() {
const [count, setCount] = useState(0)
useEffect(() => {
const handler = () => {
console.log("count changed:", count)
}
window.addEventListener("resize", handler)
return () => {
window.removeEventListener("resize", handler)
}
}, [count])
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
)
}
在这个例子中, 每次点击按钮后 count
改变, 都会重新注册 resize
事件. 这在轻量级组件中问题不大, 但如果副作用更重, 就可能引发性能问题.
优化方式:用 useRef
保持处理函数引用稳定,只注册一次
import { useEffect, useRef, useState } from "react"
function ComponentB() {
const [count, setCount] = useState(0)
const countRef = useRef(count)
useEffect(() => {
countRef.current = count
}, [count])
useEffect(() => {
const handler = () => {
console.log("count changed:", countRef.current)
}
window.addEventListener("resize", handler)
return () => {
window.removeEventListener("resize", handler)
}
}, []) // 只注册一次
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
)
}
通过将 count
放入 ref
, 并仅在 resize
时读取当前值, 可以避免副作用函数随着 count
的变化而反复注册的开销.
也可以不使用 ref
存储, 将变量的作用域提升, 脱离 React 组件, 来实现相同的功能.