Skip to content

Ink01101011/exsorted-react

Repository files navigation

exsorted-react

TypeScript-first React sorting hook powered by exsorted.

npm version npm downloads license bundle size

  • React support: 18 and 19
  • Public API: useSortedList, singleKeyAccessors, and related public types
  • Exsorted resolution: uses peer version when available, otherwise falls back to bundled dependency

Why exsorted-react

exsorted-react provides TypeScript-first React custom hooks for sorting lists with deterministic, non-mutating behavior. It is built for teams that want reusable sorting primitives instead of rewriting sort state and comparator logic per component.

Features

  • React sorting hook API for list sorting in UI state (useSortedList)
  • Helper for single-field sorting with required accessor safety (singleKeyAccessors)
  • Supports standalone, controlled, and read-only state modes
  • Works with compare-based Exsorted algorithms and custom comparators
  • Typed return controls for sort key, direction, reset, and view restore

Use Cases

  • Build sortable tables and product lists in React apps
  • Share sorting behavior across design systems and component libraries
  • Keep sorting logic strongly typed in TypeScript codebases
  • Integrate Exsorted algorithms while preserving hook ergonomics

API Surface

import { singleKeyAccessors, useSortedList } from "exsorted-react";

singleKeyAccessors

Minimal helper for one-field sorting while keeping accessors required.

const sorted = useSortedList(products, {
  accessors: singleKeyAccessors("price", (item: Product) => item.price),
  initialKey: "price",
});

useSortedList

const result = useSortedList(items, options);

Next.js SSR / RSC

useSortedList is a client hook. In Next.js App Router, call it only in Client Components.

"use client";

import { useSortedList } from "exsorted-react";

singleKeyAccessors is a pure helper and can be called in server code, but it does not make useSortedList callable in Server Components.

If external read-only state provides a key that is not present in accessors, the hook safely falls back to the first accessor key to avoid error-triggered re-render loops.

Modes

  • Standalone mode: pass accessors with optional initialKey and initialDirection
  • Controlled mode: pass sort controller object
  • Read-only state mode: pass state object

Options

  • accessors (required): map of key to value accessor
  • comparator (optional): comparator used for sorting
  • sorter (optional): custom compare-based exsorted sorter, default is mergeSort
  • initialKey and initialDirection (standalone mode only)
  • sort (controlled mode only)
  • state (read-only state mode only)

Sorter Compatibility

sorter must match compare-based signature:

(arr, compareFn?) => arr;

Not supported because function interfaces differ:

  • exsorted/non-compare: countingSort, radixSort, bucketSort, pigeonholeSort
  • exsorted/standard: introSort

Return Value

  • items: visible items (sorted or original depending on mode/actions)
  • previousItems: previous visible snapshot
  • originalItems: input source reference
  • isSorting: transition pending state
  • isSorted: true when sorted view is active and not pending
  • sort, sortKey, direction: effective sort state
  • setSortKey, setDirection, toggleDirection, setSort, reset: sort controls
  • restoreOriginal, restoreSorted: view-mode controls

Examples

Standalone Mode

import { useSortedList } from "exsorted-react";
import { quickSort } from "exsorted";

type Product = {
  id: string;
  name: string;
  price: number;
  rating: number;
};

const sorted = useSortedList(products, {
  accessors: {
    name: (item: Product) => item.name,
    price: (item: Product) => item.price,
    rating: (item: Product) => item.rating,
  },
  sorter: quickSort,
  initialKey: "price",
});

sorted.setSortKey("name");
sorted.toggleDirection();

Single-key Minimal Mode

import { singleKeyAccessors, useSortedList } from "exsorted-react";

const sorted = useSortedList(products, {
  accessors: singleKeyAccessors("price", (item: Product) => item.price),
  initialKey: "price",
});

Controlled Mode

import { useState } from "react";
import { useSortedList } from "exsorted-react";

const [sort, setSort] = useState({ key: "name" as const, direction: "asc" as const });

const sorted = useSortedList(products, {
  accessors: {
    name: (item: Product) => item.name,
    price: (item: Product) => item.price,
  },
  sort: {
    sort,
    setSort,
    reset: () => setSort({ key: "name", direction: "asc" }),
  },
});

Read-only State Mode

const sorted = useSortedList(products, {
  accessors: {
    name: (item: Product) => item.name,
    price: (item: Product) => item.price,
  },
  state: { key: "price", direction: "desc" },
});

Custom Comparator

const sorted = useSortedList(products, {
  accessors: {
    name: (item: Product) => item.name,
    price: (item: Product) => item.price,
  },
  initialKey: "name",
  comparator: (a, b) => a.name.localeCompare(b.name, "en", { sensitivity: "base" }),
});

Tree-shaking note: import and pass only the sorter algorithm you need.

About

TypeScript-first React custom hooks for list sorting with Exsorted (useSortedList, singleKeyAccessors)

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors