Created
May 19, 2020 06:31
-
-
Save daphnesmit/ece70840e059e2f8d839998eeb32184f to your computer and use it in GitHub Desktop.
Custom React Table
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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