Skip to content

Instantly share code, notes, and snippets.

@daphnesmit
Created May 19, 2020 06:31
Show Gist options
  • Save daphnesmit/ece70840e059e2f8d839998eeb32184f to your computer and use it in GitHub Desktop.
Save daphnesmit/ece70840e059e2f8d839998eeb32184f to your computer and use it in GitHub Desktop.
Custom React Table
import React, { useEffect } from 'react'
import {
CellProps,
Column,
HeaderProps,
Row,
useExpanded,
usePagination,
useRowSelect,
useSortBy,
useTable,
} from 'react-table'
import { usePrevious } from 'react-use'
import { Box, Table, TableContainer, Thead } from '@/components/atoms'
import { Pagination } from '@/components/molecules'
import { ReactTableCheckbox } from './ReactTableCheckbox'
import { ReactTableHeader } from './ReactTableHeader'
import { ReactTableLoader } from './ReactTableLoader'
import { ReactTableRow } from './ReactTableRow'
interface GetDataProps {
pageIndex: number
pageSize: number
}
interface TableProps<T extends {}> {
columns: Column<T>[]
hiddenColumns: string[]
data: T[]
isLoading: boolean
pageCount: number
hasRowSelection?: boolean
getData?: (props: GetDataProps) => void
setSelectedRows?: (row: T[]) => void
renderRowSubComponent?: (props: { row: Row<T> }) => React.ReactElement
onRowClick?: (item: T) => void
}
export function ReactTable<T extends {}>({
columns,
hiddenColumns = [],
data,
isLoading,
hasRowSelection,
pageCount: controlledPageCount,
getData,
setSelectedRows,
renderRowSubComponent,
onRowClick,
}: TableProps<T>) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
setHiddenColumns,
selectedFlatRows,
visibleColumns,
state: { pageIndex, pageSize, expanded, sortBy, selectedRowIds },
} = useTable(
{
columns,
data,
initialState: { pageIndex: 0 },
manualSortBy: true,
manualPagination: true, // Tell the usePagination
// hook that we'll handle our own data fetching
// This means we'll also have to provide our own
// pageCount.
pageCount: controlledPageCount,
},
useSortBy,
useExpanded,
usePagination,
useRowSelect,
hooks => {
if (!hasRowSelection) return
hooks.visibleColumns.push((columns: Column<T>[]) => {
const checkboxColumn = {
id: 'selection',
// Make this column a groupByBoundary.
// This ensures that groupBy columns are placed after it
groupByBoundary: true,
width: 50,
disableSortBy: true,
Header: ({ getToggleAllRowsSelectedProps }: HeaderProps<T>) => (
<Box mx="auto">
<ReactTableCheckbox {...getToggleAllRowsSelectedProps()} />
</Box>
),
Cell: ({ row }: CellProps<T>) => (
<Box mx="auto">
<ReactTableCheckbox {...row.getToggleRowSelectedProps()} />
</Box>
),
}
const newColumns = [...columns]
const position = columns[0].id == 'expander' ? 1 : 0
newColumns.splice(position, 0, checkboxColumn)
return newColumns
})
},
)
const prevHiddenColumns = usePrevious(hiddenColumns)
// Listen for changes in pagination and use the state to fetch our new data
useEffect(() => {
if (getData) getData({ pageIndex, pageSize })
}, [getData, pageIndex, pageSize])
// Set the selected rows
useEffect(() => {
const rows = selectedFlatRows.map(selectedFlatRow => selectedFlatRow.original)
if (setSelectedRows) setSelectedRows(rows)
}, [setSelectedRows, selectedFlatRows])
// Set hidden columns
useEffect(() => {
if (JSON.stringify(prevHiddenColumns) != JSON.stringify(hiddenColumns)) {
setHiddenColumns(hiddenColumns)
}
}, [hiddenColumns, prevHiddenColumns, setHiddenColumns])
return (
<>
<TableContainer>
<Table {...getTableProps()}>
<Thead>
{headerGroups.map((headerGroup, i) => (
<ReactTableHeader key={`row-header-${i}`} headerGroup={headerGroup} />
))}
</Thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<ReactTableRow
key={`row-${i}`}
row={row}
i={i}
isLoading={isLoading}
flatColumns={visibleColumns}
onRowClick={onRowClick}
renderRowSubComponent={renderRowSubComponent}
/>
)
})}
{isLoading && (!page || !page.length) && (
<ReactTableLoader columns={visibleColumns.length} />
)}
</tbody>
</Table>
</TableContainer>
<Pagination
pageIndex={pageIndex}
pageCount={pageCount}
pageSize={pageSize}
totalPages={pageOptions.length}
goToPage={(page: number) => gotoPage(page)}
onPreviousClick={() => previousPage()}
onNextClick={() => nextPage()}
canPreviousPage={canPreviousPage}
canNextPage={canNextPage}
setPageSize={(size: number) => setPageSize(size)}
/>
<pre>
<code>
{JSON.stringify(
{
pageIndex,
pageSize,
pageCount,
canNextPage,
canPreviousPage,
expanded,
sortBy,
selectedRowIds,
'selectedFlatRows[].original': selectedFlatRows.map(d => d.original),
},
null,
2,
)}
</code>
</pre>
</>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment