SCALE — Build Lab
UI部品 · REACT HOOK

ソート可能テーブルヘッダ

CATEGORYUI部品 TYPEReact Hook EFFORT30〜60分 DIFFICULTY
PRIMARY CODE
tsx
import { useState } from 'react';

export function useSortable<T>(initial: keyof T) {
  const [field, setField] = useState<keyof T>(initial);
  const [dir, setDir] = useState<'asc' | 'desc'>('asc');

  function toggle(f: keyof T) {
    if (f === field) setDir(dir === 'asc' ? 'desc' : 'asc');
    else { setField(f); setDir('asc'); }
  }

  function sort(items: T[]): T[] {
    return [...items].sort((a, b) => {
      const av = a[field], bv = b[field];
      if (av === bv) return 0;
      const cmp = av > bv ? 1 : -1;
      return dir === 'asc' ? cmp : -cmp;
    });
  }

  function arrow(f: keyof T) {
    if (f !== field) return '';
    return dir === 'asc' ? ' ↑' : ' ↓';
  }

  return { field, dir, toggle, sort, arrow };
}
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 任意のダッシュボードに組み込み

ソート可能テーブルヘッダ

:LiTarget: 用途

カラムヘッダクリックでソート方向切替。↑↓矢印表示。

:LiSparkle: 特徴

  • 昇順/降順切替
  • 矢印表示
  • 複数カラム対応
  • 型推論

:LiCode: コード(コピペ用)

import { useState } from 'react';

export function useSortable<T>(initial: keyof T) {
  const [field, setField] = useState<keyof T>(initial);
  const [dir, setDir] = useState<'asc' | 'desc'>('asc');

  function toggle(f: keyof T) {
    if (f === field) setDir(dir === 'asc' ? 'desc' : 'asc');
    else { setField(f); setDir('asc'); }
  }

  function sort(items: T[]): T[] {
    return [...items].sort((a, b) => {
      const av = a[field], bv = b[field];
      if (av === bv) return 0;
      const cmp = av > bv ? 1 : -1;
      return dir === 'asc' ? cmp : -cmp;
    });
  }

  function arrow(f: keyof T) {
    if (f !== field) return '';
    return dir === 'asc' ? ' ↑' : ' ↓';
  }

  return { field, dir, toggle, sort, arrow };
}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加