Build a usePrevious Hook and Know When Not to Use It

Build a usePrevious Hook that returns the value a variable held on the previous render, and learn when state or a key fits better.

6 min read

A usePrevious Hook returns the value a variable held during the previous render. It stores the last seen value in a ref and updates it after each commit, so callers can compare past and present inside an Effect.

It is a comparison tool, not a second source of state. A ref update never triggers a re-render, so the Hook is cheap but invisible to React.

Build the Hook

A ref holds the last value between renders, and an Effect writes the new value after each commit. That ordering is what makes the returned value lag one render behind.

App.jsxApp.jsx
import { useEffect, useRef } from "react";
 
function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  }, [value]);
  return ref.current;
}

The ref survives re-renders, unlike a plain local variable. The Effect runs after the commit, so during the current render ref.current still holds the previous value, and the return hands that older value back.

Compare past and present in an Effect

The most common use is detecting a change inside an Effect, where the previous value is safe to read.

App.jsxApp.jsx
function Player({ currentSong }) {
  const previousSong = usePrevious(currentSong);
  useEffect(() => {
    if (previousSong !== currentSong) {
      document.title = `Playing: ${currentSong}`;
    }
  }, [previousSong, currentSong]);
  return <h2>{currentSong}</h2>;
}

When the song prop changes, the comparison inside the Effect sees two different values and updates the browser tab title. The browser title is an external system, so an Effect is the right place for that work.

The previous value is read inside the Effect, not rendered. The same comparison can drive analytics or subscriptions that must react to a change.

Know when not to use it

  • Do not render the previous value, because that reads a ref during render.
  • Do not use it to setState in an Effect when a prop changes. Reset with a key instead.
  • If the past value affects rendering, store it in state during render, or derive the result directly.

A usePrevious Hook is for comparisons, not for keeping two values in sync. When the past value must appear on screen, keep the previous value in state during render instead of reading a ref that React does not track. If a prop change should reset state, pass a key to remount the component instead of comparing the old value in an Effect.

Store previous state during render when it renders

When the previous value must appear in the UI, keep it in state and set it during render. React re-renders immediately when state is set during render, so the screen never shows a stale intermediate value. This pattern is for render output, while usePrevious stays for Effect comparisons.

See how to create a custom Hook for the extraction steps, and useRef explained for how refs persist between renders.

Rune AI

Rune AI

Key Insights

  • Store the last value in a ref and update it in an Effect.
  • Read the result only inside Effects or event handlers.
  • Do not render the previous value, which reads a ref during render.
  • Do not use it to adjust state in an Effect when a prop changes.
  • Prefer a key or render-time state for past values that affect output.
RunePowered by Rune AI

Frequently Asked Questions

Why does usePrevious return the previous value?

The ref update happens in an Effect after the commit, so during the next render ref.current still holds the value from the previous render.

Can I render the previous value returned by usePrevious?

Avoid it. Rendering the result reads a ref during render, which breaks purity. Use the previous value inside an Effect or event handler instead.

When should I not use usePrevious?

Do not use it to adjust state in an Effect when a prop changes. Reset state with a key, or calculate the next value during render.

Conclusion

A usePrevious Hook stores the last seen value in a ref and updates it after each commit, so callers can compare the previous and current values inside an Effect. Use it for comparisons, not for rendering, and prefer state or a key when the past value drives output.