React useRef 保存上一次的值
FreeGuideOnline
最新
2026-07-07
jsx import { useRef, useEffect } from 'react';
function usePrevious(value) { const ref = useRef();
useEffect(() => { ref.current = value; }, [value]);
// 返回的是 ref.current,此时还没有被本次 useEffect 更新 return ref.current; }
### 使用示例
```jsx
function Counter() {
const [count, setCount] = useState(0);
const previousCount = usePrevious(count);
return (
<div>
<p>当前值: {count}</p>
<p>上一次的值: {previousCount}</p>
<button onClick={() => setCount(c => c + 1)}>+1</button>
</div>
);
}
效果说明: 当 count 从 0 变为 1 时,组件重新渲染。此时 usePrevious 返回的是 ref.current,而 ref.current 尚未被 useEffect 更新,仍然是上一次的 0。渲染结束后,useEffect 把 1 写入 ref.current,为下一次变化做准备。
深入理解:为什么不用 useState 直接存旧值?
有些开发者会尝试用另一个 state 保存旧值:
const [prev, setPrev] = useState(count);
useEffect(() => {
setPrev(count);
}, [count]);
这样做有两个明显缺陷:
- 多一次渲染:更新
prevstate 会触发额外的一次渲染,造成性能浪费和闪烁。 - 时序错乱:在 React 18 的严格模式下并发特性可能导致不可预期的中间状态。
使用 useRef 不会触发渲染,且值更新是同步的(在 useEffect 中),保证了逻辑的一致性。
进阶:让 usePrevious 支持初始默认值
上面的 usePrevious 在组件首次挂载时返回 undefined。我们可以让它接受第二个参数,作为首次渲染时的“上一次值”:
function usePrevious(value, initialValue) {
const ref = useRef(initialValue);
useEffect(() => {
ref.current = value;
});
return ref.current;
}
使用时:
const previousCount = usePrevious(count, 0); // 首次渲染时,上一次值视为0
实战应用场景
1. 检测某个 prop 的变化并执行副作用
function ProductPage({ productId }) {
const previousId = usePrevious(productId);
useEffect(() => {
if (previousId !== productId) {
console.log(`产品ID从 ${previousId} 变成了 ${productId}`);
// 发起新请求
}
}, [productId, previousId]);
// ...
}
2. 平滑过渡动画
function AnimatedValue({ value }) {
const prev = usePrevious(value) ?? value;
// 利用 prev 和 value 之间的差值做动画插值
// ...
}