Skip to content

usePreviousDistinct

A smart React hook that returns the previous distinct value of a variable,
only updating when the value meaningfully changes (based on a custom comparison function).


📘 Overview

usePreviousDistinct extends the behavior of usePrevious by introducing custom equality logic.
Instead of tracking every previous value, it only updates when the new value is distinct — helping you reduce unnecessary re-renders or side effects.


🚀 Features

  • 🧠 Tracks previous distinct values only
  • ⚙️ Supports custom equality comparison
  • 🧩 Prevents unnecessary updates
  • ✅ Works with any data type (strings, objects, numbers, etc.)
  • 🔄 Simple and dependency-free

💡 Example

tsx
import { useState, useEffect } from "react";
import usePreviousDistinct from "react-hookstack";

export default function DistinctExample() {
  const [name, setName] = useState("John");
  const prevName = usePreviousDistinct(name, (a, b) => a.toLowerCase() === b.toLowerCase());

  useEffect(() => {
    console.log("Previous distinct name:", prevName);
  }, [name, prevName]);

  return (
    <div>
      <h3>Current Name: {name}</h3>
      <h4>Previous Distinct: {prevName ?? "—"}</h4>

      <button onClick={() => setName("John")}>Set John (no change)</button>
      <button onClick={() => setName("JOHN")}>Set JOHN (no change)</button>
      <button onClick={() => setName("Jane")}>Set Jane (distinct)</button>
    </div>
  );
}

🧩 API Reference

Signature

tsx
function usePreviousDistinct<T>(
  value: T,
  isEqual?: (prev: T, next: T) => boolean
): T | undefined;

Parameters

NameTypeDescription
valueTThe current value you want to track.
isEqual(prev: T, next: T) => boolean(Optional) A comparison function that determines equality. Defaults to strict inequality (!==).

Returns

TypeDescription
T | undefinedThe previous distinct value. Returns undefined on first render or when no distinct value exists.

🧠 When to Use

  • To detect meaningful changes in data
  • When using debounced or normalized state values
  • When you need custom equality checks (e.g., case-insensitive string comparison, shallow object check)
  • To optimize side effects that depend on actual data changes

usePrevious — Tracks the previous value on every render.

⚙️ Implementation

tsx
import { useEffect, useRef } from "react";

/**
 * Returns the previous distinct value based on a custom equality check.
 */
function usePreviousDistinct<T>(
  value: T,
  isEqual?: (prev: T, next: T) => boolean
): T | undefined {
  const previousRef = useRef<T | null>(null);
  const currentRef = useRef<T | null>(null);

  useEffect(() => {
    // Update previous only when value changes meaningfully
    if (!isEqual || !isEqual(currentRef.current as T, value)) {
      previousRef.current = currentRef.current;
    }
    currentRef.current = value;
  }, [value, isEqual]);

  return previousRef?.current || undefined;
}

export default usePreviousDistinct;