Today we will create server-side paginated table with react-table. In order to see the full code you can visit my github repo.
The technologies used for this project:
- NextJS
- MongoDB
- React-Table
- React-Query
- Mongoose and mongoose-paginated-v2 plugin
And this is the finished product:
Let's have a look at the Table component.
// components/Table.js import React from 'react'; import { useTable, usePagination } from 'react-table'; function Table({ setPerPage, setPage, columns, data, currentpage, perPage, totalPage }) { const { getTableProps, getTableBodyProps, headerGroups, prepareRow, page, pageOptions, state: { pageIndex, pageSize }, } = useTable( { columns, data, useControlledState: (state) => { return React.useMemo( () => ({ ...state, pageIndex: currentpage, }), [state, currentpage] ); }, initialState: { pageIndex: currentpage }, // Pass our hoisted table state manualPagination: true, pageCount: totalPage, }, usePagination ); return ( <> <table {...getTableProps()} className="table-fixed"> <thead> {headerGroups.map((headerGroup) => ( <tr {...headerGroup.getHeaderGroupProps()}> {headerGroup.headers.slice(0, 1).map((column) => ( <th column.getHeaderProps()}> {column.render('Header')} </th> ))} {headerGroup.headers.slice(1).map((column) => ( <th {...column.getHeaderProps()} > {column.render('Header')} </th> ))} </tr> ))} </thead> <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> </> ); } export default Table; Here we render the table itself. But where the data is coming from? The parent component uses a custom react-query hook to fetch the data from api routes of NextJS.
// pages/api/schools/index.js import School from '@models/School'; import dbConnect from '@utils/dbConnect'; dbConnect(); export default async function (req, res) { switch (req.method) { case 'GET': await getSchools(req, res); break; default: res.status(400).json({ success: false }); break; } } const getSchools = async (req, res) => { try { let { page, perPage } = req.query; console.log(page, perPage); const options = { page: parseInt(page), limit: parseInt(perPage), }; const schools = await School.paginate({}, options); res.status(200).json({ success: true, data: schools, }); } catch (error) { res.status(400).json({ success: false }); } }; And custom hook for fetching data from the API Routes:
// utils/useSchools const { useQuery } = require('react-query'); const axios = require('axios'); export default function useSchools(page, perPage) { return useQuery( ['schools', page, perPage], async () => { const res = await axios.get(`/api/schools?perPage=${perPage}&page=${page}`); return res.data; }, { keepPreviousData: true } ); } The last important part is the index.js page where we call the custom hook.
import React, { useState } from 'react'; import Table from '@components/Table/Table'; import useSchools from '@utils/useSchools'; export default function Home() { const [page, setPage] = useState(1); const [perPage, setPerPage] = useState(10); const { data: schools, isLoading } = useSchools(page, perPage); const list = schools?.data.docs.map((i) => { return { col1: i.name, col2: i.il, col3: i.ilce, col4: i.kont, }; }); const data = React.useMemo(() => list, [schools]); const columns = React.useMemo( () => [ { Header: 'okul adi', accessor: 'col1', // accessor is the "key" in the data }, { Header: 'il', accessor: 'col2', }, { Header: 'ilce', accessor: 'col3', }, { Header: 'kontenjan', accessor: 'col4', }, ], [] ); if (isLoading) return <div>loading...</div>; return ( <div className="p-4 bg-white my-4 rounded shadow-xl grid"> <Table data={data} columns={columns} setPage={setPage} setPerPage={setPerPage} currentpage={page} perPage={perPage} totalPage={schools?.data.totalPages} /> </div> ); } This is a bit long code to include all of it here. So you can check the rest of it from my github repo. Cheers!!

Top comments (3)
Thank you! I thing you can make this change to improve the code
Current:
const list = schools?.data.docs.map((i) => {
return {
col1: i.name,
col2: i.il,
col3: i.ilce,
col4: i.kont,
};
});
const data = React.useMemo(() => list, [schools]);
Updated:
const data = React.useMemo(() => {
return schools?.data.docs.map((i) => {
return {
col1: i.name,
col2: i.il,
col3: i.ilce,
col4: i.kont,
};
})
}
, [schools]);
This helped me out soo much! ๐
I am glad you find it helpful Lorenso ๐