Skip to content

Commit

Permalink
chore(docs): Add example for using tanstack table with react with vir…
Browse files Browse the repository at this point in the history
…tualized rows (#513)
  • Loading branch information
kaceycleveland authored Aug 29, 2023
1 parent 5074d52 commit 976210c
Show file tree
Hide file tree
Showing 11 changed files with 1,096 additions and 1 deletion.
3 changes: 2 additions & 1 deletion docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
{ "to": "examples/react/padding", "label": "Padding" },
{ "to": "examples/react/sticky", "label": "Sticky" },
{ "to": "examples/react/infinite-scroll", "label": "Infinite Scroll" },
{ "to": "examples/react/smooth-scroll", "label": "Smooth Scroll" }
{ "to": "examples/react/smooth-scroll", "label": "Smooth Scroll" },
{ "to": "examples/react/table", "label": "Table" }
]
},
{
Expand Down
5 changes: 5 additions & 0 deletions examples/react/table/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
6 changes: 6 additions & 0 deletions examples/react/table/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Example

To run this example:

- `npm install` or `npm`
- `npm run start` or `npm run start`
12 changes: 12 additions & 0 deletions examples/react/table/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
24 changes: 24 additions & 0 deletions examples/react/table/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "tanstack-react-virtual-example-dynamic",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview --port 3001",
"start": "vite"
},
"dependencies": {
"@faker-js/faker": "7.6.0",
"@tanstack/react-table": "^8.7.9",
"@tanstack/react-virtual": "3.0.0-beta.49",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.8",
"@vitejs/plugin-react": "^2.2.0",
"vite": "^3.2.3"
}
}
19 changes: 19 additions & 0 deletions examples/react/table/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
*,
*:before,
*:after {
box-sizing: border-box;
}

html {
font-family: sans-serif;
font-size: 14px;
}

body {
padding: 1rem;
}

.container {
height: 600px;
overflow: auto;
}
186 changes: 186 additions & 0 deletions examples/react/table/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import * as React from 'react'
import { createRoot } from 'react-dom/client'

import { useVirtualizer } from '@tanstack/react-virtual'
import {
ColumnDef,
flexRender,
getCoreRowModel,
getSortedRowModel,
Row,
SortingState,
useReactTable,
} from '@tanstack/react-table'
import { makeData, Person } from './makeData'
import './index.css'

function ReactTableVirtualized() {
const [sorting, setSorting] = React.useState<SortingState>([])

const columns = React.useMemo<ColumnDef<Person>[]>(
() => [
{
accessorKey: 'id',
header: 'ID',
size: 60,
},
{
accessorKey: 'firstName',
cell: info => info.getValue(),
},
{
accessorFn: row => row.lastName,
id: 'lastName',
cell: info => info.getValue(),
header: () => <span>Last Name</span>,
},
{
accessorKey: 'age',
header: () => 'Age',
size: 50,
},
{
accessorKey: 'visits',
header: () => <span>Visits</span>,
size: 50,
},
{
accessorKey: 'status',
header: 'Status',
},
{
accessorKey: 'progress',
header: 'Profile Progress',
size: 80,
},
{
accessorKey: 'createdAt',
header: 'Created At',
cell: info => info.getValue<Date>().toLocaleString(),
},
],
[]
)

const [data, setData] = React.useState(() => makeData(50_000))

const table = useReactTable({
data,
columns,
state: {
sorting,
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
debugTable: true,
})

const {rows} = table.getRowModel();


const parentRef = React.useRef<HTMLDivElement>(null)

const virtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 34,
overscan: 20
});

return (
<div ref={parentRef} className="container">
<div style={{ height: `${virtualizer.getTotalSize()}px`}}>
<table>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<th
key={header.id}
colSpan={header.colSpan}
style={{ width: header.getSize() }}
>
{header.isPlaceholder ? null : (
<div
{...{
className: header.column.getCanSort()
? 'cursor-pointer select-none'
: '',
onClick: header.column.getToggleSortingHandler(),
}}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{{
asc: ' 🔼',
desc: ' 🔽',
}[header.column.getIsSorted() as string] ?? null}
</div>
)}
</th>
)
})}
</tr>
))}
</thead>
<tbody>
{virtualizer.getVirtualItems().map((virtualRow, index) => {
const row = rows[virtualRow.index] as Row<Person>
return (
<tr key={row.id}
style={{
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start - index * virtualRow.size}px)`,
}}>
{row.getVisibleCells().map(cell => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
</div></div>
)
}

function App() {
return (
<div>
<p>
For tables, the basis for the offset of the translate css function is from the row's initial position itself. Because of this, we need to calculate the translateY pixel count different and base it off the the index.
</p>
<ReactTableVirtualized />
<br />
<br />
{process.env.NODE_ENV === 'development' ? (
<p>
<strong>Notice:</strong> You are currently running React in
development mode. Rendering performance will be slightly degraded
until this application is build for production.
</p>
) : null}
</div>
)
}

const container = document.getElementById('root')
const root = createRoot(container!)
const { StrictMode } = React

root.render(
<StrictMode>
<App />
</StrictMode>,
)
50 changes: 50 additions & 0 deletions examples/react/table/src/makeData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { faker } from '@faker-js/faker'

export type Person = {
id: number
firstName: string
lastName: string
age: number
visits: number
progress: number
status: 'relationship' | 'complicated' | 'single'
createdAt: Date
}

const range = (len: number) => {
const arr: number[] = []
for (let i = 0; i < len; i++) {
arr.push(i)
}
return arr
}

const newPerson = (index: number): Person => {
return {
id: index + 1,
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
age: faker.datatype.number(40),
visits: faker.datatype.number(1000),
progress: faker.datatype.number(100),
createdAt: faker.datatype.datetime({ max: new Date().getTime() }),
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((d): Person => {
return {
...newPerson(d),
}
})
}

return makeDataLevel()
}
12 changes: 12 additions & 0 deletions examples/react/table/tsconfig.dev.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"composite": true,
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./build/types"
},
"files": ["src/main.tsx"],
"include": [
"src"
// "__tests__/**/*.test.*"
]
}
7 changes: 7 additions & 0 deletions examples/react/table/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()]
})
Loading

0 comments on commit 976210c

Please sign in to comment.