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]);

这样做有两个明显缺陷:

  • 多一次渲染:更新 prev state 会触发额外的一次渲染,造成性能浪费和闪烁。
  • 时序错乱:在 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 之间的差值做动画插值
  // ...
}