All files / hooks/use-previous/src usePrevious.ts

100% Statements 7/7
100% Branches 4/4
100% Functions 1/1
100% Lines 7/7

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68                                                                                                          19x 19x   19x       19x 7x 7x     19x    
import { useRef } from "react";
 
/**
 * Comparator used to decide whether the tracked value changed between renders.
 * Return `true` when `previous` and `current` should be considered equal (in
 * which case the stored "previous" value is left untouched). Defaults to
 * `Object.is`.
 */
export type UsePreviousComparator<T> = (previous: T, current: T) => boolean;
 
/**
 * Returns the previous **distinct** value — the value from the last render in
 * which it actually changed, not simply the value from the render before this
 * one.
 *
 * On the first render there is no previous value, so it returns `undefined`.
 * By default values are compared with `Object.is`, so a value that never
 * changes keeps `undefined` as its "previous". This "previous distinct value"
 * semantic is what change-detection logic usually wants, but it differs from a
 * naive "value one render ago": if `value` is unchanged across a re-render, the
 * returned previous does **not** advance.
 *
 * Pass a custom `isEqual` to ignore changes that are referentially different
 * but semantically equal (e.g. new object literals with the same fields).
 *
 * @param value - The value to track
 * @param isEqual - Optional equality comparator (defaults to `Object.is`)
 * @returns The previous distinct value, or `undefined`
 *
 * @example
 * ```tsx
 * const [count, setCount] = useState(0);
 * const prev = usePrevious(count);
 * return <p>{prev} → {count}</p>;
 * ```
 *
 * @example
 * ```tsx
 * // Ignore new-but-equal objects
 * const prev = usePrevious(user, (a, b) => a.id === b.id);
 * ```
 *
 * @remarks
 * This tracks the previous *distinct* value (updated during render), which is
 * correct under StrictMode's double-invoked render. Under concurrent rendering,
 * a render React discards can still advance the stored value, so in rare cases
 * the "previous" may reflect a value from an interrupted (never committed)
 * render. For the common synchronous case this is not observable.
 */
export function usePrevious<T>(
  value: T,
  isEqual?: UsePreviousComparator<T>
): T | undefined {
  const currentRef = useRef<T>(value);
  const previousRef = useRef<T | undefined>(undefined);
 
  const equal = isEqual
    ? isEqual(currentRef.current, value)
    : Object.is(currentRef.current, value);
 
  if (!equal) {
    previousRef.current = currentRef.current;
    currentRef.current = value;
  }
 
  return previousRef.current;
}