import React from 'react' import ReactDOM from 'react-dom/client' import { keepPreviousData, QueryClient, QueryClientProvider, useQuery, } from '@tanstack/react-query' import './index.css' import { PaginationState, useReactTable, getCoreRowModel, ColumnDef, flexRender, } from '@tanstack/react-table' // import { fetchData, Person } from './fetchData' const queryClient = new QueryClient() function App() { const rerender = React.useReducer(() => ({}), {})[1] const columns = React.useMemo<ColumnDef<Person>[]>( () => [ { header: 'Name', footer: props => props.column.id, columns: [ { accessorKey: 'firstName', cell: info => info.getValue(), footer: props => props.column.id, }, { accessorFn: row => row.lastName, id: 'lastName', cell: info => info.getValue(), header: () => <span>Last Name</span>, footer: props => props.column.id, }, ], }, { header: 'Info', footer: props => props.column.id, columns: [ { accessorKey: 'age', header: () => 'Age', footer: props => props.column.id, }, { header: 'More Info', columns: [ { accessorKey: 'visits', header: () => <span>Visits</span>, footer: props => props.column.id, }, { accessorKey: 'status', header: 'Status', footer: props => props.column.id, }, { accessorKey: 'progress', header: 'Profile Progress', footer: props => props.column.id, }, ], }, ], }, ], [] ) const [pagination, setPagination] = React.useState<PaginationState>({ pageIndex: 0, pageSize: 10, }) const dataQuery = useQuery({ queryKey: ['data', pagination], queryFn: () => fetchData(pagination), placeholderData: keepPreviousData, // don't have 0 rows flash while changing pages/loading next page }) const defaultData = React.useMemo(() => [], []) const table = useReactTable({ data: dataQuery.data?.rows ?? defaultData, columns, // pageCount: dataQuery.data?.pageCount ?? -1, //you can now pass in `rowCount` instead of pageCount and `pageCount` will be calculated internally (new in v8.13.0) rowCount: dataQuery.data?.rowCount, // new in v8.13.0 - alternatively, just pass in `pageCount` directly state: { pagination, }, onPaginationChange: setPagination, getCoreRowModel: getCoreRowModel(), manualPagination: true, //we're doing manual "server-side" pagination // getPaginationRowModel: getPaginationRowModel(), // If only doing manual pagination, you don't need this debugTable: true, }) return ( <div className="p-2"> <div className="h-2" /> <table> <thead> {table.getHeaderGroups().map(headerGroup => ( <tr key={headerGroup.id}> {headerGroup.headers.map(header => { return ( <th key={header.id} colSpan={header.colSpan}> {header.isPlaceholder ? null : ( <div> {flexRender( header.column.columnDef.header, header.getContext() )} </div> )} </th> ) })} </tr> ))} </thead> <tbody> {table.getRowModel().rows.map(row => { return ( <tr key={row.id}> {row.getVisibleCells().map(cell => { return ( <td key={cell.id}> {flexRender( cell.column.columnDef.cell, cell.getContext() )} </td> ) })} </tr> ) })} </tbody> </table> <div className="h-2" /> <div className="flex items-center gap-2"> <button className="border rounded p-1" onClick={() => table.firstPage()} disabled={!table.getCanPreviousPage()} > {'<<'} </button> <button className="border rounded p-1" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()} > {'<'} </button> <button className="border rounded p-1" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()} > {'>'} </button> <button className="border rounded p-1" onClick={() => table.lastPage()} disabled={!table.getCanNextPage()} > {'>>'} </button> <span className="flex items-center gap-1"> <div>Page</div> <strong> {table.getState().pagination.pageIndex + 1} of{' '} {table.getPageCount().toLocaleString()} </strong> </span> <span className="flex items-center gap-1"> | Go to page: <input type="number" min="1" max={table.getPageCount()} defaultValue={table.getState().pagination.pageIndex + 1} onChange={e => { const page = e.target.value ? Number(e.target.value) - 1 : 0 table.setPageIndex(page) }} className="border p-1 rounded w-16" /> </span> <select value={table.getState().pagination.pageSize} onChange={e => { table.setPageSize(Number(e.target.value)) }} > {[10, 20, 30, 40, 50].map(pageSize => ( <option key={pageSize} value={pageSize}> Show {pageSize} </option> ))} </select> {dataQuery.isFetching ? 'Loading...' : null} </div> <div> Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} {dataQuery.data?.rowCount.toLocaleString()} Rows </div> <div> <button onClick={() => rerender()}>Force Rerender</button> </div> <pre>{JSON.stringify(pagination, null, 2)}</pre> </div> ) } const rootElement = document.getElementById('root') if (!rootElement) throw new Error('Failed to find the root element') ReactDOM.createRoot(rootElement).render( <React.StrictMode> <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider> </React.StrictMode> )
import React from 'react' import ReactDOM from 'react-dom/client' import { keepPreviousData, QueryClient, QueryClientProvider, useQuery, } from '@tanstack/react-query' import './index.css' import { PaginationState, useReactTable, getCoreRowModel, ColumnDef, flexRender, } from '@tanstack/react-table' // import { fetchData, Person } from './fetchData' const queryClient = new QueryClient() function App() { const rerender = React.useReducer(() => ({}), {})[1] const columns = React.useMemo<ColumnDef<Person>[]>( () => [ { header: 'Name', footer: props => props.column.id, columns: [ { accessorKey: 'firstName', cell: info => info.getValue(), footer: props => props.column.id, }, { accessorFn: row => row.lastName, id: 'lastName', cell: info => info.getValue(), header: () => <span>Last Name</span>, footer: props => props.column.id, }, ], }, { header: 'Info', footer: props => props.column.id, columns: [ { accessorKey: 'age', header: () => 'Age', footer: props => props.column.id, }, { header: 'More Info', columns: [ { accessorKey: 'visits', header: () => <span>Visits</span>, footer: props => props.column.id, }, { accessorKey: 'status', header: 'Status', footer: props => props.column.id, }, { accessorKey: 'progress', header: 'Profile Progress', footer: props => props.column.id, }, ], }, ], }, ], [] ) const [pagination, setPagination] = React.useState<PaginationState>({ pageIndex: 0, pageSize: 10, }) const dataQuery = useQuery({ queryKey: ['data', pagination], queryFn: () => fetchData(pagination), placeholderData: keepPreviousData, // don't have 0 rows flash while changing pages/loading next page }) const defaultData = React.useMemo(() => [], []) const table = useReactTable({ data: dataQuery.data?.rows ?? defaultData, columns, // pageCount: dataQuery.data?.pageCount ?? -1, //you can now pass in `rowCount` instead of pageCount and `pageCount` will be calculated internally (new in v8.13.0) rowCount: dataQuery.data?.rowCount, // new in v8.13.0 - alternatively, just pass in `pageCount` directly state: { pagination, }, onPaginationChange: setPagination, getCoreRowModel: getCoreRowModel(), manualPagination: true, //we're doing manual "server-side" pagination // getPaginationRowModel: getPaginationRowModel(), // If only doing manual pagination, you don't need this debugTable: true, }) return ( <div className="p-2"> <div className="h-2" /> <table> <thead> {table.getHeaderGroups().map(headerGroup => ( <tr key={headerGroup.id}> {headerGroup.headers.map(header => { return ( <th key={header.id} colSpan={header.colSpan}> {header.isPlaceholder ? null : ( <div> {flexRender( header.column.columnDef.header, header.getContext() )} </div> )} </th> ) })} </tr> ))} </thead> <tbody> {table.getRowModel().rows.map(row => { return ( <tr key={row.id}> {row.getVisibleCells().map(cell => { return ( <td key={cell.id}> {flexRender( cell.column.columnDef.cell, cell.getContext() )} </td> ) })} </tr> ) })} </tbody> </table> <div className="h-2" /> <div className="flex items-center gap-2"> <button className="border rounded p-1" onClick={() => table.firstPage()} disabled={!table.getCanPreviousPage()} > {'<<'} </button> <button className="border rounded p-1" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()} > {'<'} </button> <button className="border rounded p-1" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()} > {'>'} </button> <button className="border rounded p-1" onClick={() => table.lastPage()} disabled={!table.getCanNextPage()} > {'>>'} </button> <span className="flex items-center gap-1"> <div>Page</div> <strong> {table.getState().pagination.pageIndex + 1} of{' '} {table.getPageCount().toLocaleString()} </strong> </span> <span className="flex items-center gap-1"> | Go to page: <input type="number" min="1" max={table.getPageCount()} defaultValue={table.getState().pagination.pageIndex + 1} onChange={e => { const page = e.target.value ? Number(e.target.value) - 1 : 0 table.setPageIndex(page) }} className="border p-1 rounded w-16" /> </span> <select value={table.getState().pagination.pageSize} onChange={e => { table.setPageSize(Number(e.target.value)) }} > {[10, 20, 30, 40, 50].map(pageSize => ( <option key={pageSize} value={pageSize}> Show {pageSize} </option> ))} </select> {dataQuery.isFetching ? 'Loading...' : null} </div> <div> Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} {dataQuery.data?.rowCount.toLocaleString()} Rows </div> <div> <button onClick={() => rerender()}>Force Rerender</button> </div> <pre>{JSON.stringify(pagination, null, 2)}</pre> </div> ) } const rootElement = document.getElementById('root') if (!rootElement) throw new Error('Failed to find the root element') ReactDOM.createRoot(rootElement).render( <React.StrictMode> <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider> </React.StrictMode> )
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.