Getting Started with TanStack
TanStack is a popular framework for building table data layers. However, RevoGrid offers a more comprehensive solution by incorporating all the features of TanStack, along with advanced rendering, virtual scrolling, and column-level optimizations for superior performance. If you're transitioning from an existing TanStack architecture or want to quickly prototype, this guide will help you integrate TanStack with RevoGrid.
We strongly recommend using RevoGrid's native features for the best performance. It is specifically designed to handle advanced rendering and optimize every aspect of DOM manipulation, ensuring an efficient experience. RevoGrid operates on the column level, which allows for superior performance, whereas TanStack manages data at the row level.
The following example shows how to use TanStack with RevoGrid to create columns, bind data, and manage interactions.
Source code Git
<template>
<VGrid
:source="source"
:columns="rvColumns"
hide-attribution
@beforeedit="handleEdit"
/>
</template>
<script setup lang="ts">
import { VGrid, type ColumnProp } from '@revolist/vue3-datagrid'
import {
useVueTable,
getCoreRowModel,
type ColumnDef,
type RowData,
} from '@tanstack/vue-table'
import { computed, ref } from 'vue'
import { tanstackToRvGridColumns } from '../tanstack/utils'
import { makeData, type Person } from '../makeData'
declare module '@tanstack/vue-table' {
interface TableMeta<TData extends RowData> {
updateData: (
rowIndex: number,
columnId: ColumnProp,
value: unknown
) => void
}
}
// Define columns according tanstack guide
const columns: ColumnDef<Person>[] = [
{
header: 'Name',
columns: [
{
accessorKey: 'firstName',
header: 'First Name',
},
{
accessorKey: 'lastName',
header: 'Last Name',
},
],
},
]
const data = ref<Person[]>(makeData(5))
// define tanstack table
const table = useVueTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
meta: {
updateData: (rowIndex: number, columnId: ColumnProp, value: any) => {
data.value = data.value.map((row, index) => {
if (index === rowIndex) {
return {
...data.value[rowIndex]!,
[columnId]: value,
}
}
return row
})
},
},
})
// get data from tanstack table
const source = computed(() => {
const rows = table.getRowModel().flatRows
return rows
})
const rvColumns = computed(() => {
// transform tanstack to revogrid format
return tanstackToRvGridColumns(table.getAllColumns())
})
function handleEdit(e: CustomEvent<HTMLRevoGridElementEventMap['beforeedit']>) {
e.preventDefault()
table.options.meta?.updateData(
e.detail.model.index,
e.detail.prop,
e.detail.val
)
}
</script>
import React, { useState, useMemo, useCallback } from 'react';
import { RevoGrid, type BeforeSaveDataDetails, type RevoGridCustomEvent } from '@revolist/react-datagrid';
import {
useReactTable,
getCoreRowModel,
type ColumnDef,
} from '@tanstack/react-table';
import { tanstackToRvGridColumns } from '../tanstack/utils';
import { makeData, type Person } from '../makeData';
// Define TanStack columns
const columns: ColumnDef<Person>[] = [
{
header: 'Name',
columns: [
{
accessorKey: 'firstName',
header: 'First Name',
},
{
accessorKey: 'lastName',
header: 'Last Name',
},
],
},
];
const ExampleIntegration = () => {
const [data, setData] = useState(makeData(5));
// Set up TanStack table instance
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
meta: {
updateData: (rowIndex, columnId, value) => {
setData((old) =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value,
};
}
return row;
})
);
},
},
});
// Extract data and columns for RevoGrid
const source = useMemo(() => {
const rows = table.getRowModel().flatRows;
return rows;
}, [table]);
const rvColumns = useMemo(() => {
// Convert TanStack column definition to RevoGrid format
return tanstackToRvGridColumns(table.getAllColumns());
}, [table]);
const handleEdit = useCallback((e: RevoGridCustomEvent<BeforeSaveDataDetails>) => {
e.preventDefault();
const { model, prop, val } = e.detail;
table.options.meta?.updateData(model.index, prop, val);
}, [table]);
return (
<RevoGrid
source={source}
columns={rvColumns}
onBeforeedit={handleEdit}
hideAttribution
/>
);
};
export default ExampleIntegration;
// Helper functions to convert tanstack to revogrid format
import type { ColumnGrouping, ColumnRegular } from "@revolist/vue3-datagrid";
import type { Column } from "@tanstack/vue-table";
export function tanstackToRvGridColumns(tanstackColumns: Column<any>[]) {
return tanstackColumns.map((col) => {
// Check if this column has children (group columns)
if (col.columns?.length) {
const rvGroup: ColumnGrouping = {
name: col.columnDef.header,
children: tanstackToRvGridColumns(col.columns),
};
return rvGroup; // Recursive call for child columns
}
const rvColumn: ColumnRegular = {
prop: col.id, // Use col.id or accessorKey
name: col.columnDef.header, // Direct mapping for column header
sortable: !!col.getCanSort(), // Check if the column is sortable
filter: col.getCanFilter() ? "customFilterType" : false, // Handle filters
};
// Handle custom sorting functions via cellCompare
const sortingFn = col.getSortingFn();
if (!!sortingFn) {
rvColumn.cellCompare = (_, rowA: any, rowB: any) =>
sortingFn(rowA, rowB, col.id);
rvColumn.sortable = true; // Ensure sorting is enabled
}
// Handle filtering logic if provided
const filterFn = col.getFilterFn();
if (filterFn) {
rvColumn.filter = "customFilterType"; // Assume custom type for filtering
}
rvColumn.cellParser = (model) => {
return model.renderValue(col.id);
};
return rvColumn;
});
}
import { faker } from '@faker-js/faker'
export type Person = {
avatar: string
firstName: string
lastName: string
age: number
visits: number
progress: number
status: 'relationship' | 'complicated' | 'single'
subRows?: Person[]
}
const range = (len: number) => {
const arr: number[] = []
for (let i = 0; i < len; i++) {
arr.push(i)
}
return arr
}
const newPerson = (): Person => {
return {
avatar: faker.image.avatar(),
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
age: faker.number.int(40),
visits: faker.number.int(1000),
progress: faker.number.int(100),
status: faker.helpers.shuffle<Person['status']>([
'relationship',
'complicated',
'single',
])[0]!,
}
}
export function makeData(...lens: number[]) {
const makeDataLevel = (depth = 0): Person[] => {
const len = lens[depth]!
return range(len).map((): Person => {
return {
...newPerson(),
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
}
})
}
return makeDataLevel()
}
Summary
Integrating TanStack with RevoGrid allows you to leverage the row-level operations provided by TanStack, while taking advantage of RevoGrid's optimized performance and advanced rendering capabilities, including virtual scrolling and efficient DOM handling. For a smooth migration or quick experimentation, this setup provides a great foundation, but remember, RevoGrid's built-in features are crafted for peak performance and user experience.