Skip to content

React 列排序

点击表头排序图标切换 升序 → 降序 → 取消:列上声明 sortable: true 即显示排序图标;sortMode: 'multiple' 时按住 Shift 点击可叠加多列排序(表头角标显示优先级)。

  • defaultSort 声明初始排序
  • sortMethod 自定义比较器(本地化字符串、业务优先级等),返回值由排序方向自动取反
  • ref 上的 sort() / clearSort() / getSortState() 编程式控制,onSortChange 监听变化

使用的 API

ts
type Column = {
  key: string;  // 列标识(必填)
  title: string;  // 列标题(必填)
  width: number;  // 列宽(必填)
  sortable?: boolean;  // 显示排序图标,点击图标排序
  defaultSort?: 'asc' | 'desc';  // 初始排序
  sortMethod?: (a: Row, b: Row) => number;  // 自定义比较器(升序语义)
}

type Options = {
  list: T[];  // 数据列表
  itemKey: string;  // 行唯一标识字段名
  estimatedSize: number;  // 行预估高度(px)
  sortMode?: 'single' | 'multiple';  // 单列 / Shift 多列,默认 single
  onSortChange?: (state: SortSpec[]) => void;  // 排序变化回调
}

// 实例方法(ref 上调用)
tableRef.current?.sort(colKey, 'asc' | 'desc' | null);  // null 取消该列
tableRef.current?.clearSort();
tableRef.current?.getSortState();  // [{ colKey, order }]

注意

sortMode 为初始化选项,运行时切换需重建实例——示例里用 key={sortMode} 让 React 重建 VirtTableReact。表格存在合并单元格(spanMethod 产生的合并)时排序会被忽略。

完整语义(三态循环、多列优先级、与合并/筛选的关系)见 Vanilla · 列排序

示例

微应用尚未挂载。

源码

点击查看源码
tsx
import React from 'react';
import {
  VirtTableReact,
  type ReactTableColumn,
  type VirtTableRef,
  type SortSpec,
} from '@virt-table/react';
import { faker } from '@faker-js/faker';

interface Row extends Record<string, unknown> {
  id: number;
  name: string;
  age: number;
  score: number;
  level: string;
}

const ROW_COUNT = 2000;
const LEVELS = ['S', 'A', 'B', 'C'];
const LEVEL_RANK: Record<string, number> = { S: 0, A: 1, B: 2, C: 3 };

const columns: ReactTableColumn<Row>[] = [
  { key: 'id', title: 'ID', width: 80, sortable: true, defaultSort: 'asc' },
  {
    key: 'name',
    title: '姓名',
    width: 180,
    sortable: true,
    // 自定义比较器:按中文/英文本地化规则比较
    sortMethod: (a, b) => a.name.localeCompare(b.name, 'zh-Hans-CN'),
  },
  { key: 'age', title: '年龄', width: 100, sortable: true },
  { key: 'score', title: '分数', width: 100, sortable: true },
  {
    key: 'level',
    title: '等级',
    width: 100,
    sortable: true,
    // 自定义比较器:按业务顺序 S > A > B > C,而不是字典序
    sortMethod: (a, b) => LEVEL_RANK[a.level]! - LEVEL_RANK[b.level]!,
  },
];

const list: Row[] = Array.from({ length: ROW_COUNT }, (_, i) => ({
  id: i + 1,
  name: faker.person.fullName(),
  age: faker.number.int({ min: 18, max: 65 }),
  score: faker.number.int({ min: 0, max: 100 }),
  level: LEVELS[faker.number.int({ min: 0, max: 3 })]!,
}));

export default function SortTable() {
  const tableRef = React.useRef<VirtTableRef>(null);
  const [sortMode, setSortMode] = React.useState<'single' | 'multiple'>('multiple');
  const [sortState, setSortState] = React.useState<SortSpec[]>([]);

  const status = sortState.length
    ? `当前排序:${sortState.map((s, i) => `${i + 1}. ${s.colKey} ${s.order}`).join(' · ')}`
    : '当前排序:无';

  return (
    <div className="demo-wrapper">
      <h3 className="demo-title">React 列排序</h3>
      <div className="demo-hint">
        点击表头的排序图标切换 <b>升序 → 降序 → 取消</b>;多列模式下按住 <b>Shift</b>{' '}
        点击可叠加排序列(表头角标显示优先级)。「姓名」按本地化规则比较,「等级」按 S &gt; A &gt; B &gt; C
        业务顺序比较。
      </div>
      <div className="virt-table-controls">
        <label>
          排序模式
          <select
            value={sortMode}
            onChange={(e) => setSortMode(e.target.value as 'single' | 'multiple')}
          >
            <option value="multiple">multiple(多列)</option>
            <option value="single">single(单列)</option>
          </select>
        </label>
        <button
          type="button"
          className="virt-table-btn"
          onClick={() => tableRef.current?.sort('score', 'desc')}
        >
          按分数降序
        </button>
        <button type="button" className="virt-table-btn" onClick={() => tableRef.current?.clearSort()}>
          清除排序
        </button>
      </div>
      <div className="status-text">{status}</div>
      <div style={{ width: 660, height: 480 }} className="demo-container">
        {/* sortMode 是初始化选项,切换时靠 key 重建表格实例 */}
        <VirtTableReact
          key={sortMode}
          ref={tableRef}
          columns={columns}
          options={{
            list,
            itemKey: 'id',
            estimatedSize: 40,
            buffer: 6,
            border: true,
            sortMode,
            onSortChange: setSortState,
          }}
        />
      </div>
    </div>
  );
}