REACT Table select rows not creating checkboxes correctly - reactjs

I am new to REACT and having trouble getting a table component to work correctly.
I am using the react-tables package and the tutorial example here but I am doing everything in a table.js component file and adding it to my App.js. Basically, I am trying to create a table with pagination, sorting, and selectable rows with checkboxes. My issue is that I am only getting the checkboxes to populate in the header (see below).
Here is my code; what am I doing incorrectly here? Note - my rows are being created using page, not row and are coming from my API. Any suggestions on how to fix this?
import React, { useMemo, useEffect, useState } from 'react'
import reactTable, {useTable, useRowSelect, usePagination, useSortBy} from 'react-table'
import axios from "axios";
import { COLUMNS } from './columns'
import './Table.css'
export const Table = () => {
const [loadingData, setLoadingData] = useState(true);
const columns = useMemo(() => COLUMNS, []);
const [data, setData] = useState([]);
const IndeterminateCheckbox = React.forwardRef(
//This is the function for the checkboxes in page select
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef()
const resolvedRef = ref || defaultRef
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate
}, [resolvedRef, indeterminate])
return (
<>
<input type="checkbox" ref={resolvedRef} {...rest} />
</>
)
}
)
useEffect(() => {
async function getData() {
await axios
.get("http://localhost:5000/my/api")
.then((response) => {
// check if the data is populated
console.log(response.data);
setData(response.data);
// you tell it that you had the result
setLoadingData(false);
});
}
if (loadingData) {
// if the result is not ready so you make the axios call
getData();
}
}, []);
const tableInstance = useTable({
columns,
data,
initialState: { pageIndex: 2 }
}, useSortBy,
usePagination,
useRowSelect,
hooks => {
hooks.visibleColumns.push(columns => [
// Let's make a column for selection
{
id: 'selection',
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllPageRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllPageRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
])
}
);
const { getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page, //Begins the pagination and select stuff
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
selectedFlatRows,
state: { pageIndex, pageSize, selectedRowIds }} = tableInstance
return (
<div className="container">
{loadingData ? (
<p>Loading Please wait...</p>
) : (
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{
headerGroup.headers.map((column) =>(
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')}
<span>
{column.isSorted
? column.isSortedDesc
? ' 🔽'
: ' 🔼'
: ''}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{
page.map((row, i) => {
prepareRow(row);
return(
<tr {...row.getRowProps()}>
{
row.cells.map(cell => {
console.log(cell.render('Cell').props.value);
//<td {...cell.getCellProps()}>{cell.render('Cell').props.value}</td>
return <td>{cell.render('Cell').props.value}</td>
})
}
</tr>
)
})
}
</tbody>
</table>
)}
<div className="pagination">
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
{'<<'}
</button>{' '}
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
{'<'}
</button>{' '}
<button onClick={() => nextPage()} disabled={!canNextPage}>
{'>'}
</button>{' '}
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
{'>>'}
</button>{' '}
<span>
Page{' '}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{' '}
</span>
<span>
| Go to page:{' '}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0
gotoPage(page)
}}
style={{ width: '100px' }}
/>
</span>{' '}
</div>
</div>
)
}

Return <td {...cell.getCellProps()}>{cell.render('Cell')}</td> from tr.
This should work:
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td> // fix return statement
);
})}
</tr>
);
})}
</tbody>

Related

How can I Mark Initial value of Checkbox Checked of a single row in React tabl

I am getting a list of Products from an API call and I am rendering it through react-table.
const getItemsList=()=>{
getAuthorization()
.get("product/all_products/")
.then((res) => {
setProducts(res.data);
dispatch(setAPIDetailsItemsTable(res.data));
})
.catch((err) => {
console.log("Error in getting products", err);
});
}
useEffect(() => {
// Get all products
getItemsList();
}, []);
I am using redux for state Management
const data = APIDetailsItemsTable
const columns = React.useMemo(
() => [
{
Header: "Number",
accessor: "product_id",
},
{
Header: "Item Name",
accessor: "name",
},
{
Header: "Item Type",
accessor: "product_type",
},
{
Header: "Status",
accessor: "status",
Cell: StatusPill
}
],
[]
);
return (
< AddGuideItemstable
columns={columns}
data={data == undefined ? [] : data}
text="Undefined" />
)
/>
Here is the table component. I am working on Editing part. The checkbox can select only one item from the list.
function AddGuideItemstable({ columns, data, text }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
visibleColumns,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state,
preGlobalFilteredRows,
setGlobalFilter,
selectedFlatRows,
state: { selectedRowIds }
} = useTable(
{
columns,
data,
initialState: {selectedRowIds},
stateReducer: (newState, action) => {
if (action.type === "toggleRowSelected") {
newState.selectedRowIds = {
[action.id]: true,
}
}
return newState;
},
},
useFilters, // useFilters!
useGlobalFilter,
useSortBy,
usePagination, // new
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => {
return [
...columns,
{
Header: "Choose Items",
id: "selection",
Cell: ({ row }) => (
<div className="flex flex-col ml-6">
<CheckBox {...row.getToggleRowSelectedProps()}/>
</div>
),
//// dispatching selected item ID if changed
useEffect(() => {
let Id = selectedFlatRows.map(
d => d.original.id)
dispatch(setAddGuideItemID(Id))
console.log("selected row id", Id)
}, [selectedRowIds])
// Render the UI for your table
return (
<>
<table
{...getTableProps()}
className="min-w-full bg-transparent divide-y divide-gray-200"
>
<thead className=" border-b-8 border-white">
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
// Add the sorting props to control sorting. For this example
// we can add them into the header props
<th
scope="col"
className="group px-2 py-3 text-center text-sm font-medium text-gray-400 font-Roboto tracking-wider"
{...column.getHeaderProps(
column.getSortByToggleProps()
)}
>
<div className="flex items-center justify-between">
{column.render("Header")}
{/* Add a sort direction indicator */}
<span>
{column.isSorted ? (
column.isSortedDesc ? (
<SortDownIcon className="w-4 h-4 text-gray-100" />
) : (
<SortUpIcon className="w-4 h-4 text-gray-100" />
)
) : (
<SortIcon className="w-4 h-4 text-gray-100 opacity-0 group-hover:opacity-100" />
)}
</span>
</div>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()} className="bg-white">
{page.map((row, index) => {
// new
prepareRow(row);
return (
<tr
{...row.getRowProps()}
className={
index % 2 === 0
? "bg-cyan-100 border-b-8 border-white"
: "bg-white border-b-8 border-white"
}
>
{row.cells.map((cell) => {
return (
<td
{...cell.getCellProps()}
className="pr-1 pl-2 whitespace-nowrap"
role="cell"
>
{cell.column.Cell.name === "defaultRenderer" ? (
<div className="text-sm font-semibold text-black">
{cell.render("Cell")}
</div>
) : (
cell.render("Cell")
)}
</td>
);
})}
</tr>
);
})}
</tbody>
The checkbox component is as below
import react, { forwardRef, useEffect, useRef, useState } from "react";
export const CheckBox = forwardRef(({ indeterminate, ...rest }, ref) => {
const defaultRef = useRef();
const resolvedRef = ref || defaultRef;
useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<>
<div class="flex items-center">
<input
type="checkbox"
ref={resolvedRef}
{...rest}
id="A3-yes"
name="A3-confirmation"
class="opacity-0 absolute h-8 w-8"
/>
</>
);
});
I have an ID stored in a state
let ID = useSelector((state)=> state.guide.AddGuideResID)
What I want to do is if " ID " in the state is equal to the "ID in the products list" then mark the checkbox checked for that row.
I want to achieve like this. I want to mark only single item thats original ID matches the ID in my redux state. Checkbox checked manually

Custom pagination - React table

I want to make a custom pagination using react-table. It needs to look quite a specific way:
Currently it looks like this, I guess this is the default for the current pagination I am using:
I need it to look like this:
My code is the following:
import React, { useEffect, useState } from 'react';
import { useTable, usePagination } from 'react-table';
import { TableProps } from '../../../types';
export const Table: React.FC<TableProps> = ({
columns,
data,
isLoading,
hasPagination = true,
pageNumber,
onPageChange,
maxPage,
onRowClick = () => undefined,
maxCount,
}) => {
const [canPreviousPage, setCanPreviousPage] = useState(false);
const [canNextPage, setCanNextPage] = useState(false);
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
pageCount,
nextPage,
previousPage,
} = useTable(
{
columns,
data,
manualPagination: true,
pageCount: maxPage,
},
usePagination,
);
useEffect(() => {
if (pageNumber <= 1) {
setCanPreviousPage(false);
} else {
setCanPreviousPage(true);
}
if (maxPage > pageNumber) {
setCanNextPage(true);
} else {
setCanNextPage(false);
}
}, [pageNumber, maxPage]);
const onNextPage = () => {
nextPage();
onPageChange(pageNumber + 1);
};
const onPreviousPage = () => {
previousPage();
onPageChange(pageNumber - 1);
};
const Pagination = () => {
if (!hasPagination) {
return null;
}
return (
<div className="pagination flex items-center text-black mt-3">
<Button onClick={onPreviousPage} disabled={!canPreviousPage}>
<Icon className={canPreviousPage ? 'color-black' : 'color-grey-200'}>chevron_left</Icon>
</Button>
<span>
Page{' '}
<strong>
{pageNumber} of {pageCount}
</strong>
</span>
<Button onClick={onNextPage} disabled={!canNextPage}>
<Icon className={canNextPage ? 'color-black' : 'color-grey-200'}>chevron_right</Icon>
</Button>
<div>{maxCount} records found</div>
<div className="mx-1">
<ActivityLoader isLoading={isLoading} />
</div>
</div>
);
};
return (
<div className="pt-10 pl-20 pr-16 font-nunito font-medium text-base">
<div className="align-middle inline-block w-full w-40 text-left">
<div className="shadow border-b border-gray-200">
<table {...getTableProps} className="w-full divide-y divide-gray-200">
<thead className="bg-mainBlue p-16">
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
scope="col"
className="px-6 py-4 text-left font-medium text-gray-500 p-16 bg-mainBlue text-base"
{...column.getHeaderProps()}
>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()} className="bg-white divide-y divide-gray-200">
{page.map((row) => {
prepareRow(row);
return (
<tr {...row.getRowProps()} onClick={() => onRowClick(_.get(row, 'original.id'))}>
{row.cells.map((cell) => {
return (
<td
className="px-6 py-4 whitespace-nowrap text-black"
{...cell.getCellProps()}
>
{cell.render('Cell')}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
</div>
<Pagination />
</div>
);
};
Can anyone help me to customize my pagination?? I have been reading some docs but I am struggling to implement a solution
First, create a new file called usePaginationPages.js
import { useCallback, useEffect, useMemo, useState } from "react";
export const usePaginationPages = ({ gotoPage, length, pageSize }) => {
const [currentPage, setCurrentPage] = useState(1);
const totalPages = useMemo(() => {
return Math.ceil(length / pageSize);
}, [length, pageSize]);
const canGo = useMemo(() => {
return {
next: currentPage < totalPages,
previous: currentPage - 1 > 0
};
}, [currentPage, totalPages]);
// currentPage siblings
const pages = useMemo(() => {
const start = Math.floor((currentPage - 1) / 5) * 5;
const end = start + 5 > totalPages ? totalPages : start + 5;
return Array.from({ length: end - start }, (_, i) => start + i + 1);
}, [currentPage, totalPages]);
// programatically call gotoPage when currentPage changes
useEffect(() => {
gotoPage(currentPage - 1);
}, [currentPage, gotoPage]);
// show first page when per page select options changes
useEffect(() => {
if (pageSize) {
goTo(1);
}
}, [pageSize]);
const goTo = (pg) => {
setCurrentPage(pg);
};
const goNext = useCallback(() => {
if (canGo.next) {
setCurrentPage((prev) => prev + 1);
}
}, [canGo]);
const goPrev = useCallback(() => {
if (canGo.previous) {
setCurrentPage((prev) => prev - 1);
}
}, [canGo]);
return {
canGo,
currentPage,
pages,
goTo,
goNext,
goPrev
};
};
Next, create Pagination.js by implementing our usePaginationPages hook
import { useState, useEffect, memo } from "react";
import { ChevronLeftIcon, ChevronRightIcon } from "#heroicons/react/24/solid";
import { usePaginationPages } from "./usePaginationPages";
function Pagination({ gotoPage, length, pageSize, setPageSize }) {
const [perPage, setPerPage] = useState(pageSize);
const {
canGo,
currentPage,
pages,
goTo,
goNext,
goPrev
} = usePaginationPages({
gotoPage,
length,
pageSize
});
// update pageSize when perPage changes
useEffect(() => {
// don't forget set to Number
setPageSize(Number(perPage));
}, [perPage, setPageSize]);
return (
<div className="m-4 flex items-center justify-center">
<button
onClick={goPrev}
disabled={!canGo.previous}
className="m-1 px-2 py-1 border rounded-md"
>
<ChevronLeftIcon className="h-6 w-4 text-blue-500" />
</button>
{pages.map((page, i) => (
<button
onClick={() => goTo(page)}
key={i}
style={{
background: currentPage === page ? "blue" : "none",
color: currentPage === page ? "white" : "black"
}}
className="m-1 px-3 py-1 border rounded-md"
>
{page}
</button>
))}
<button
onClick={goNext}
disabled={!canGo.next}
className="m-1 px-2 py-1 border rounded-md"
>
<ChevronRightIcon className="h-6 w-4 text-blue-500" />
</button>
<select
className="px-2 py-[6px] border rounded-md w-30 bg-white"
value={pageSize}
onChange={(e) => setPerPage(e.target.value)}
>
{[10, 50, 100].map((pageSize) => (
<option className="py-2" value={pageSize} key={pageSize}>
{pageSize} / page
</option>
))}
</select>
</div>
);
}
export default memo(Pagination);
You can see the full code here:
Javascript version:
Typescript version:

React table number of selected rows

I am trying to find out how to get the number of selected rows from react table.
Also I would like to send the number of selected rows into another sibling component, which would enable or disable based on the number of rows selected(minimum of 10).
Please Help. I would also be helpful if anyone could design a modal where I can edit a value of a selected row (only if a row is selected) in the table with value showing in edit modal.
import React, { useState } from 'react'
import { useMemo } from 'react'
import table from '../assets/json/mock.json'
import { useTable,useRowSelect, useSortBy, usePagination} from 'react-table';
import {useSticky} from 'react-table-sticky'
const Table =({columns,data})=> {
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef()
const resolvedRef = ref || defaultRef
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate
}, [resolvedRef, indeterminate])
return (
<>
<input type="checkbox" ref={resolvedRef} {...rest} />
</>
)
}
)
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
page,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
state,
gotoPage,
pageCount,
setPageSize,
selectedFlatRows,
prepareRow,
}=useTable({
columns,
data,
initialState : {pageIndex : 0}
},
useSortBy,usePagination,useRowSelect,
hooks => {
hooks.visibleColumns.push(columns => [
// Let's make a column for selection
{
id: 'selection',
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
])
}
)
const {pageIndex,pageSize,selectedRowIds}=state
return (
<>
<table className="database-table sticky" {...getTableProps()}>
<thead className='header'>
{
headerGroups.map((headerGroup)=>
(
<tr {...headerGroup.getHeaderGroupProps()}>
{
headerGroup.headers.map((column) =>
(
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render(`Header`)}
<span>
{
column.isSorted ? (column.isSortedDesc ? 'â–¼':'â–²'):''
}
</span>
</th>
))
}
</tr>
))
}
</thead>
<tbody {...getTableBodyProps()}>
{
page.map((row)=>
{
prepareRow(row)
return(
<tr {...row.getRowProps()}>
{row.cells.map((cell)=>{
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})
}
</tbody>
</table>
<div className='header-bottom'>
{
headerGroups.map((headerGroup)=>
(
<tr {...headerGroup.getHeaderGroupProps()}>
{
headerGroup.headers.map((column) =>
(
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render(`Header`)}
<span>
{
column.isSorted ? (column.isSortedDesc ? 'â–¼':'â–²'):''
}
</span>
</th>
))
}
</tr>
))
}
</div>
<div className='table-footer'>
<div className='page-no'id='modal-item'>
Viewing : {pageIndex+1} of {pageOptions.length}
</div>
<span className ='copyright'>
© 2022 Highradius.All Rights Reserved
</span>
<span className='rowno' id='modal-item'>
Rows per Page :
<select value={pageSize} onChange={e=>setPageSize(Number(e.target.value))}>
{
[10,20,30,40,50].map(pageSize=>(
<option key={pageSize} value={pageSize}>
{pageSize }
</option>
))
}
</select>
</span>
<button button onClick={()=>previousPage()} disabled={!canPreviousPage} id='pag-btn'>{' < '}</button>
<button button onClick={()=>nextPage()} disabled={!canNextPage} id='pag-btn' >{' > '}</button>
</div>
</>
)
}
export default Table
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Add
selectedFlatRows,
state: { selectedRowIds },
after prepareRow, in const.
You can print the selected rows like this.
<p>Selected Rows: {Object.keys(selectedRowIds).length}</p>
<pre>
<code>
{JSON.stringify(
{
selectedRowIds: selectedRowIds,
'selectedFlatRows[].original': selectedFlatRows.map(d => d.original),
},
null,
2
)}
</code>
</pre>
A working example is here: https://codesandbox.io/s/naughty-pond-3e5jp

React-table combination of rows and pagination doesn't function as intended

I'm using react-table to include the rows and pagination in the table as shown in this example: https://react-table.tanstack.com/docs/examples/row-selection-and-pagination.
I've done my example as the tutorial says, but it doesn't function as intended. I can only select 1 row before it stops updating the selectedRowsId. Deselecting the row doesn't clear it either. What am I doing wrong?
import React, { useMemo, forwardRef, useRef, useEffect } from "react";
import {
useTable,
useSortBy,
useFilters,
useGlobalFilter,
usePagination,
useRowSelect
} from "react-table";
import matchSorter from "match-sorter";
import '../../static/scss/table.scss'
function DefaultColumnFilter({
column: { filterValue, preFilteredRows, setFilter },
}) {
const count = preFilteredRows.length;
return (
<input
value={filterValue || ""}
onChange={(e) => {
setFilter(e.target.value || undefined); // Set undefined to remove the filter entirely
}}
placeholder={`Search ${count} records...`}
/>
);
}
function SelectColumnFilter({
column: { filterValue, setFilter, preFilteredRows, id },
}) {
// Calculate the options for filtering
// using the preFilteredRows
const options = React.useMemo(() => {
const options = new Set();
preFilteredRows.forEach((row) => {
options.add(row.values[id]);
});
return [...options.values()];
}, [id, preFilteredRows]);
// Render a multi-select box
return (
<select
value={filterValue}
onChange={(e) => {
setFilter(e.target.value || undefined);
}}
>
<option value="">All</option>
{options.map((option, i) => (
<option key={i} value={option}>
{option}
</option>
))}
</select>
);
}
function fuzzyTextFilterFn(rows, id, filterValue) {
return matchSorter(rows, filterValue, { keys: [(row) => row.values[id]] });
}
fuzzyTextFilterFn.autoRemove = (val) => !val;
const IndeterminateCheckbox = forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = useRef()
const resolvedRef = ref || defaultRef
useEffect(() => {
resolvedRef.current.indeterminate = indeterminate
}, [resolvedRef, indeterminate])
return (
<>
<input type="checkbox" ref={resolvedRef} {...rest} />
</>
)
}
)
export default function Table({ data }) {
console.log(data[0].title);
const filterTypes = useMemo(
() => ({
// Add a new fuzzyTextFilterFn filter type.
fuzzyText: fuzzyTextFilterFn,
// Or, override the default text filter to use
// "startWith"
text: (rows, id, filterValue) => {
return rows.filter((row) => {
const rowValue = row.values[id];
return rowValue !== undefined
? String(rowValue)
.toLowerCase()
.startsWith(String(filterValue).toLowerCase())
: true;
});
},
}),
[]
);
const defaultColumn = useMemo(
() => ({
// Let's set up our default Filter UI
Filter: DefaultColumnFilter,
}),
[]
);
const columns = useMemo(
() => [
{
Header: "Naziv",
accessor: "title",
},
{
Header: "Tip",
accessor: "activity_type_id",
Filter: SelectColumnFilter,
filter: "includes",
},
{
Header: "Datum",
accessor: "start_time",
},
{
Header: "Mjesto",
accessor: "location",
},
{
Header: "Organizator",
accessor: "team_id",
Filter: SelectColumnFilter,
filter: "includes",
},
{
Header: "Odgovorna osoba",
accessor: "user_id",
},
],
[]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
prepareRow,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
selectedFlatRows,
state: { pageIndex, pageSize, selectedRowIds },
} = useTable(
{
columns,
data,
defaultColumn,
filterTypes,
initialState: { pageIndex: 0 },
},
useFilters,
useGlobalFilter,
useSortBy,
usePagination,
useRowSelect,
hooks => {
hooks.visibleColumns.push(columns => [
// Let's make a column for selection
{
id: 'selection',
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllPageRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllPageRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
])
}
);
return (
<div>
<legend className="legend">Popis aktivnosti</legend>
<>
<pre>
<code>
{JSON.stringify(
{
pageIndex,
pageSize,
pageCount,
canNextPage,
canPreviousPage,
},
null,
2
)}
</code>
</pre>
<table {...getTableProps()}>
<thead>
{
// Loop over the header rows
headerGroups.map((headerGroup) => (
// Apply the header row props
<tr {...headerGroup.getHeaderGroupProps()}>
{
// Loop over the headers in each row
headerGroup.headers.map((column) => (
// Apply the header cell props
<th
{...column.getHeaderProps(
column.getSortByToggleProps()
)}
>
{
// Render the header
column.render("Header")
}
<span>
{column.isSorted
? column.isSortedDesc
? " 🔽"
: " 🔼"
: ""}
</span>
</th>
))
}
{}
<th>Nige</th>
<th>Nei</th>
</tr>
))
}
{
// Loop over the header rows
headerGroups.map((headerGroup) => (
// Apply the header row props
<tr {...headerGroup.getHeaderGroupProps()}>
{
// Loop over the headers in each row
headerGroup.headers.map((column) => (
// Apply the header cell props
<th>
<div>
{column.canFilter ? column.render("Filter") : null}
</div>
</th>
))
}
{}
<th></th>
<th></th>
</tr>
))
}
</thead>
{/* Apply the table body props */}
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
<div className="pagination">
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
{"<<"}
</button>{" "}
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
{"<"}
</button>{" "}
<button onClick={() => nextPage()} disabled={!canNextPage}>
{">"}
</button>{" "}
<button
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
>
{">>"}
</button>{" "}
<span>
Page{" "}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{" "}
</span>
<span>
| Go to page:{" "}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: "100px" }}
/>
</span>{" "}
<select
value={pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
<p>Selected Rows: {Object.keys(selectedRowIds).length}</p>
<pre>
<code>
{JSON.stringify(
{
selectedRowIds: selectedRowIds,
'selectedFlatRows[].original': selectedFlatRows.map(
d => d.original
),
},
null,
2
)}
</code>
</pre>
</>
</div>
);
}
I found the solution. index.js file has the <React.StrictMode> wrapping the <App />. Removing the <React.StrictMode> fixes it and functions properly. This is probably a bug and should be fixed.

react-table Reinitializing on Render

I'm having an issue where react-table (version 7.1.0) seems to be reinitializing any time the page needs to be re-rendered. Using the code below (running example here) as an example, if you were to change the pageIndex value (by switching to a different page), then hit Dummy Button, you can observe that the pageIndex resets back to its default value of 0. The same thing happens if you modify the pageSize in that it automatically resets back to its default value of 10 any time the page has to be re-rendered.
import React, { useState } from "react";
import makeData from "./makeData";
import { useTable, usePagination } from "react-table";
import { ButtonToolbar, Button, Table } from "react-bootstrap";
// Nonsense function to force page to be rendered
function useForceUpdate() {
const [value, setValue] = useState(0);
return () => setValue(value => ++value);
}
export default function App() {
const forceUpdate = useForceUpdate();
const columns = React.useMemo(
() => [
{
Header: "Name",
columns: [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Last Name",
accessor: "lastName"
}
]
},
{
Header: "Info",
columns: [
{
Header: "Age",
accessor: "age"
},
{
Header: "Visits",
accessor: "visits"
},
{
Header: "Status",
accessor: "status"
},
{
Header: "Profile Progress",
accessor: "progress"
}
]
}
],
[]
);
const data = React.useMemo(() => makeData(100000), []);
let ArchiveTable = ({ columns, data }) => {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { pageIndex, pageSize }
} = useTable(
{
columns,
data
},
usePagination
);
return (
<div style={{ textAlign: "center" }}>
<Table striped bordered {...getTableProps()} className="datasets">
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row);
return (
<tr
{...row.getRowProps()}
className={row.isSelected ? "selected" : row.className}
>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
</Table>
<div className="pagination" style={{ display: "inline-block" }}>
<ButtonToolbar>
<Button
variant="light"
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
size="small"
>
<span><<</span>
</Button>
<Button
variant="light"
onClick={previousPage}
disabled={!canPreviousPage}
size="small"
>
<span><</span>
</Button>
<select
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value));
}}
>
{[5, 10, 20, 30, 40, 50].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
<Button
variant="light"
onClick={nextPage}
disabled={!canNextPage}
size="small"
>
<span>></span>
</Button>
<Button
variant="light"
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
size="small"
>
<span>>></span>
</Button>
</ButtonToolbar>
<span>
Page <strong>{pageOptions.length === 0 ? 0 : pageIndex + 1}</strong>{" "}
of <strong>{pageOptions.length}</strong>
</span>
</div>
</div>
);
};
return (
<div className="App">
<ArchiveTable data={data} columns={columns} />
<button onClick={forceUpdate}>Dummy Button</button>
</div>
);
}
I'm at a complete loss for what to do to fix this. What is the proper way to set everything up so that I don't reinitialize react-table every time the page has to be re-rendered? Said another way, if I hit the Dummy Button, I don't want the table to reset back to page 1 with a page size of 10.
Ended up figuring out the answer. The problem was I needed to move the initialization of the table outside of the rendering logic (quite obvious, in hindsight). Basically, I simply created an ArchiveTable function. For anybody who stumbles across this, you can check here for a working example.
function ArchiveTable({ columns, data }) {
// Add all the initialization and table rendering code here
// (everything that was originally part of the ArchiveTable initialization)
}

Resources