Custom sortingFn not being run when row key is undefined
還沒有人認領這個 Issue。
- 主要語言
- TypeScript
- 星號
- 28.4k
- 分支
- 3.6k
- 平均合併
- 1 天 14 小時
- 30 天內合併 PR
- 10
描述
TanStack Table version
8.19.2
Framework/Library version
Vue 3.4.31
Describe the bug and the steps to reproduce it
I was debating whether to even post this issue or not, since it's such a narrow edge case and is mostly inconsequential. But here it is.
My custom sortingFn is was not being run when changing the sort direction of certain columns. I was adding things to the sort func to handle the order of nullish values, and it wasn't having any effect because the function wasn't even being run. Luckily this isn't super important, as the sortUndefined met my needs in this case, but I still think this is a bug.
I can't nail down the exact conditions that cause this, but I think this happens when:
- there are only 2 rows
- one of the rows object doesn't have the key that is being sorted by
See the reproducible example. Look at the console logs while clicking on the different columns. The first static column triggers the sorting func. The other two columns do not, unless you add a third row or define the key for the other row.
Reproducible example code for posterity
<template>
<table>
<thead>
<tr v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
<th
v-for="header in headerGroup.headers"
:key="header.id"
:colSpan="header.colSpan"
@click="header.column.getToggleSortingHandler()?.($event)"
>
<FlexRender
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
<span v-if="header.column.getIsSorted() === 'desc'"> v</span>
<span v-else-if="header.column.getIsSorted() === 'asc'"> ^</span>
<span v-else> -</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in table.getRowModel().rows" :key="row.id">
<td v-for="cell in row.getVisibleCells()" :key="cell.id">
<FlexRender
:render="cell.column.columnDef.cell"
:props="cell.getContext()"
/>
</td>
</tr>
</tbody>
</table>
</template>
<script lang="ts">
type Cell = Record<string, unknown>;
export type Cols<Rows extends Cell[] = Cell[]> = {
key: Extract<keyof Rows[number], string>;
name: string;
}[];
</script>
<script setup lang="ts" generic="Rows extends Cell[]">
import { computed } from "vue";
import {
createColumnHelper,
FlexRender,
getCoreRowModel,
getFacetedMinMaxValues,
getFacetedRowModel,
getFacetedUniqueValues,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useVueTable,
type Row as TanstackRow,
} from "@tanstack/vue-table";
import type { SortingFn } from "@tanstack/vue-table";
type Props = {
cols: Cols<Rows>;
rows: Rows;
};
const props = defineProps<Props>();
type Row = Rows[number];
const columnHelper = createColumnHelper<Row>();
const sortingFunction: SortingFn<Row> = (
a: TanstackRow<Row>,
b: TanstackRow<Row>,
) => {
console.log("sort");
if (a < b) return -1;
if (a > b) return 1;
return 0;
};
const columns = computed(() =>
props.cols.map((col) =>
columnHelper.accessor((row: Row) => row[col.key], {
id: col.key,
header: col.name,
enableSorting: true,
sortingFn: sortingFunction,
}),
),
);
const table = useVueTable({
get data() {
return props.rows;
},
get columns() {
return columns.value;
},
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
getFacetedMinMaxValues: getFacetedMinMaxValues(),
initialState: {
pagination: {
pageIndex: 0,
pageSize: 999999,
},
},
});
</script>
<script setup lang="ts">
import AppTable from "./components/AppTable.vue";
const rows = [
{ static: "test", abc: 123 },
{ static: "test", def: 456 },
// { static: "test", abc: 789 },
];
const cols = [
{ key: "static", name: "Static" },
{ key: "abc", name: "ABC" },
{ key: "def", name: "DEF" },
];
</script>
<template>
<AppTable :rows="rows" :cols="cols" />
</template>
Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
https://codesandbox.io/p/devbox/tanstack-table-sorting-bug-782h9d
Do you intend to try to help solve this bug with your own PR?
No, because I do not know how
Terms & Code of Conduct
- I agree to follow this project's Code of Conduct
- I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
sortUndefined met my needs here, but I was also able to force the sortingFn to run by making sure every row being returned from get data() { return props.rows; } has every col key defined:
const _rows = computed(() => {
/** row with all possible object keys at least defined */
const blankRow = Object.fromEntries(props.cols.map((col) => [col.key, ""]));
return props.rows.map((row) => ({
/** start with blank row */
...cloneDeep(blankRow), // lodash cloneDeep
/** add actual row data */
...row,
}));
});
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
研究方向
從連結的 CodeSandbox 重現與 getSortedRowModel 排序路徑開始,比較列中各列的資料列省略了所存取索引鍵的情況與靜態欄位。對這些欄位變更排序方向時會呼叫自訂 sortingFn,並涵蓋兩個資料列具有 undefined-key 的情況,即視為完成。
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- typescript
- 領域
- frontend
- Issue 類型
- 缺陷
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 活躍度
- 停滯
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100