为 React 实现分页 Material table

Implement pagination for React Material table

我有这个 Spring 用于列出数据库项目的引导端点:

import React, { useEffect, useState } from "react";

import clsx from "clsx";
import {
  createStyles,
  lighten,
  makeStyles,
  Theme,
} from "@material-ui/core/styles";
import CircularProgress from "@material-ui/core/CircularProgress";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TablePagination from "@material-ui/core/TablePagination";
import TableRow from "@material-ui/core/TableRow";
import TableSortLabel from "@material-ui/core/TableSortLabel";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper";
import Checkbox from "@material-ui/core/Checkbox";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";
import DeleteIcon from "@material-ui/icons/Delete";
import FilterListIcon from "@material-ui/icons/FilterList";
import axios, { AxiosResponse } from "axios";
import { getTask } from "../../service/merchants";

const baseUrl = "http://185.185.126.15:8080/api";

interface OnboardingTaskDto {
  id?: number;
  name: string;
}

async function getTask(
  page: number,
  size: number
): Promise<AxiosResponse<OnboardingTaskDto[]>> {
  return await axios.get<OnboardingTaskDto[]>(
    `${baseUrl}/management/onboarding/task?page=${page}&size=${size}`
  );
}

interface Data {
  id: number;
  businessName: string;
  title: string;
  status: string;
}

function createData(
    id: number,
    businessName: string,
    title: string,
    status: string
): Data {
  return { id, businessName, title, status };
}

function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
  if (b[orderBy] < a[orderBy]) {
    return -1;
  }
  if (b[orderBy] > a[orderBy]) {
    return 1;
  }
  return 0;
}

type Order = "asc" | "desc";

function getComparator<Key extends keyof any>(
    order: Order,
    orderBy: Key
): (
    a: { [key in Key]: number | string },
    b: { [key in Key]: number | string }
) => number {
  return order === "desc"
      ? (a, b) => descendingComparator(a, b, orderBy)
      : (a, b) => -descendingComparator(a, b, orderBy);
}

function stableSort<T>(array: T[], comparator: (a: T, b: T) => number) {
  const stabilizedThis = array.map((el, index) => [el, index] as [T, number]);
  stabilizedThis.sort((a, b) => {
    const order = comparator(a[0], b[0]);
    if (order !== 0) return order;
    return a[1] - b[1];
  });
  return stabilizedThis.map((el) => el[0]);
}

interface HeadCell {
  disablePadding: boolean;
  id: keyof Data;
  label: string;
  numeric: boolean;
}

const headCells: HeadCell[] = [
  { id: "id", numeric: false, disablePadding: true, label: "id" },
  {
    id: "businessName",
    numeric: true,
    disablePadding: false,
    label: "businessName",
  },
  { id: "title", numeric: true, disablePadding: false, label: "title" },
  { id: "status", numeric: true, disablePadding: false, label: "status" },
];

interface EnhancedTableProps {
  classes: ReturnType<typeof useStyles>;
  numSelected: number;
  onRequestSort: (
      event: React.MouseEvent<unknown>,
      property: keyof Data
  ) => void;
  onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void;
  order: Order;
  orderBy: string;
  rowCount: number;
}

function EnhancedTableHead(props: EnhancedTableProps) {
  const {
    classes,
    onSelectAllClick,
    order,
    orderBy,
    numSelected,
    rowCount,
    onRequestSort,
  } = props;
  const createSortHandler =
      (property: keyof Data) => (event: React.MouseEvent<unknown>) => {
        onRequestSort(event, property);
      };

  return (
      <TableHead>
        <TableRow>
          <TableCell padding="checkbox">
            <Checkbox
                indeterminate={
                  numSelected > 0 && numSelected < rowCount
                }
                checked={rowCount > 0 && numSelected === rowCount}
                onChange={onSelectAllClick}
                inputProps={{ "aria-label": "select all desserts" }}
            />
          </TableCell>
          {headCells.map((headCell) => (
              <TableCell
                  key={headCell.id}
                  align={headCell.numeric ? "right" : "left"}
                  padding={headCell.disablePadding ? "none" : "normal"}
                  sortDirection={orderBy === headCell.id ? order : false}
              >
                <TableSortLabel
                    active={orderBy === headCell.id}
                    direction={orderBy === headCell.id ? order : "asc"}
                    onClick={createSortHandler(headCell.id)}
                >
                  {headCell.label}
                  {orderBy === headCell.id ? (
                      <span className={classes.visuallyHidden}>
                                    {order === "desc"
                                        ? "sorted descending"
                                        : "sorted ascending"}
                                </span>
                  ) : null}
                </TableSortLabel>
              </TableCell>
          ))}
        </TableRow>
      </TableHead>
  );
}

const useToolbarStyles = makeStyles((theme: Theme) =>
    createStyles({
      root: {
        paddingLeft: theme.spacing(2),
        paddingRight: theme.spacing(1),
      },
      highlight:
          theme.palette.type === "light"
              ? {
                color: theme.palette.secondary.main,
                backgroundColor: lighten(
                    theme.palette.secondary.light,
                    0.85
                ),
              }
              : {
                color: theme.palette.text.primary,
                backgroundColor: theme.palette.secondary.dark,
              },
      title: {
        flex: "1 1 100%",
      },
    })
);

interface EnhancedTableToolbarProps {
  numSelected: number;
  onClick: (e: React.MouseEvent<unknown>) => void;
}

const EnhancedTableToolbar = (props: EnhancedTableToolbarProps) => {
  const classes = useToolbarStyles();
  const { numSelected } = props;

  return (
      <Toolbar
          className={clsx(classes.root, {
            [classes.highlight]: numSelected > 0,
          })}
      >
        {numSelected > 0 ? (
            <Typography
                className={classes.title}
                color="inherit"
                variant="subtitle1"
                component="div"
            >
              {numSelected} selected
            </Typography>
        ) : (
            <Typography
                className={classes.title}
                variant="h6"
                id="tableTitle"
                component="div"
            >
              Customers
            </Typography>
        )}
        {numSelected > 0 ? (
            <Tooltip title="Delete">
              <IconButton aria-label="delete" onClick={props.onClick}>
                <DeleteIcon />
              </IconButton>
            </Tooltip>
        ) : (
            <Tooltip title="Filter list">
              <IconButton aria-label="filter list">
                <FilterListIcon />
              </IconButton>
            </Tooltip>
        )}
      </Toolbar>
  );
};

const useStyles = makeStyles((theme: Theme) =>
    createStyles({
      root: {
        width: "100%",
      },
      paper: {
        width: "100%",
        marginBottom: theme.spacing(2),
      },
      table: {
        minWidth: 750,
      },
      visuallyHidden: {
        border: 0,
        clip: "rect(0 0 0 0)",
        height: 1,
        margin: -1,
        overflow: "hidden",
        padding: 0,
        position: "absolute",
        top: 20,
        width: 1,
      },
    })
);

export default function BusinessCustomersTable() {
  const classes = useStyles();
  const [order, setOrder] = React.useState<Order>("asc");
  const [orderBy, setOrderBy] = React.useState<keyof Data>("businessName");
  const [selected, setSelected] = React.useState<number[]>([]);
  const [page, setPage] = React.useState(0);
  const [rowsPerPage, setRowsPerPage] = React.useState(5);
  const [rows, setRows] = useState<Data[]>([]);
  const [loading, setLoading] = useState(false);

  let updatedState: Data[] = [];

  // TODO - move this to API file
  const apiUrl = "http://185.185.126.15:8080/api/management/onboarding/task";

  useEffect(() => {
    const getData = async () => {
      setLoading(true);

      getTask(1, 100)
          .then((resp) => {
            console.log(resp.data);
          })
          .catch((error) => {
            console.error(error);
          });
      const response = await axios.get(apiUrl, {
        params: { page: 1, size: 100 },
      });
      setLoading(false);

      const objContent: any = response.data.content;

      for (let a = 0; a < objContent.length; a++) {
        updatedState[a] = createData(
            objContent[a].id,
            objContent[a].businessName,
            objContent[a].title,
            objContent[a].status
        );

        setRows([...rows, ...updatedState]);
      }
    };

    getData();
  }, []);

  const handleRequestSort = (
      event: React.MouseEvent<unknown>,
      property: keyof Data
  ) => {
    const isAsc = orderBy === property && order === "asc";
    setOrder(isAsc ? "desc" : "asc");
    setOrderBy(property);
  };

  const handleSelectAllClick = (
      event: React.ChangeEvent<HTMLInputElement>
  ) => {
    if (event.target.checked) {
      const newSelecteds = rows.map((n) => n.id);
      setSelected(newSelecteds);
      return;
    }
    setSelected([]);
  };

  const handleClick = (event: React.MouseEvent<unknown>, id: number) => {
    const selectedIndex = selected.indexOf(id);
    let newSelected: number[] = [];

    if (selectedIndex === -1) {
      newSelected = newSelected.concat(selected, id);
    } else if (selectedIndex === 0) {
      newSelected = newSelected.concat(selected.slice(1));
    } else if (selectedIndex === selected.length - 1) {
      newSelected = newSelected.concat(selected.slice(0, -1));
    } else if (selectedIndex > 0) {
      newSelected = newSelected.concat(
          selected.slice(0, selectedIndex),
          selected.slice(selectedIndex + 1)
      );
    }

    setSelected(newSelected);
  };

  const handleChangePage = (event: unknown, newPage: number) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (
      event: React.ChangeEvent<HTMLInputElement>
  ) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  const handleDeleteClick = async () => {
    // npm install qs
    var qs = require("qs");

    const response = await axios.delete(apiUrl, {
      params: {
        ids: selected,
      },
      paramsSerializer: (params) => {
        return qs.stringify(params);
      },
    });

    if (response.status === 204) {
      const updatedData = rows.filter(
          (row) => !selected.includes(row.id)
      ); // It'll return all data except selected ones

      setRows(updatedData); // reset rows to display in table.
    }
  };

  const isSelected = (id: number) => selected.indexOf(id) !== -1;

  const emptyRows =
      rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);

  return (
      <div className={classes.root}>
        <Paper className={classes.paper}>
          <EnhancedTableToolbar
              numSelected={selected.length}
              onClick={handleDeleteClick}
          />
          <TableContainer>
            <Table
                className={classes.table}
                aria-labelledby="tableTitle"
                aria-label="enhanced table"
            >
              <EnhancedTableHead
                  classes={classes}
                  numSelected={selected.length}
                  order={order}
                  orderBy={orderBy}
                  onSelectAllClick={handleSelectAllClick}
                  onRequestSort={handleRequestSort}
                  rowCount={rows.length}
              />
              <TableBody>
                {loading ? (
                    <div className="spinerr">
                      <CircularProgress />
                    </div>
                ) : null}
                {stableSort(rows, getComparator(order, orderBy))
                    .slice(
                        page * rowsPerPage,
                        page * rowsPerPage + rowsPerPage
                    )
                    .map((row, index) => {
                      const isItemSelected = isSelected(row.id);
                      const labelId = `enhanced-table-checkbox-${index}`;

                      return (
                          <TableRow
                              hover
                              onClick={(event) =>
                                  handleClick(event, row.id)
                              }
                              role="checkbox"
                              aria-checked={isItemSelected}
                              tabIndex={-1}
                              key={row.businessName}
                              selected={isItemSelected}
                          >
                            <TableCell padding="checkbox">
                              <Checkbox
                                  checked={isItemSelected}
                                  inputProps={{
                                    "aria-labelledby":
                                    labelId,
                                  }}
                              />
                            </TableCell>
                            <TableCell
                                component="th"
                                id={labelId}
                                scope="row"
                                padding="none"
                            >
                              {row.id}
                            </TableCell>
                            <TableCell align="right">
                              {row.businessName}
                            </TableCell>
                            <TableCell align="right">
                              {row.title}
                            </TableCell>
                            <TableCell align="right">
                              {row.status}
                            </TableCell>
                          </TableRow>
                      );
                    })}
                {emptyRows > 0 && (
                    <TableRow style={{ height: 53 * emptyRows }}>
                      <TableCell colSpan={6} />
                    </TableRow>
                )}
              </TableBody>
            </Table>
          </TableContainer>
          <TablePagination
              rowsPerPageOptions={[5, 10, 25]}
              component="div"
              count={rows.length}
              rowsPerPage={rowsPerPage}
              page={page}
              onPageChange={handleChangePage}
              onRowsPerPageChange={handleChangeRowsPerPage}
          />
        </Paper>
      </div>
  );
}

沙盒:https://stackblitz.com/edit/react-ts-tnpk85?file=Hello.tsx

当我第一次加载数据并切换页面时,我没有看到对后端的额外请求。看起来数据 table 行数据只加载一次。我需要实现惰性分页并在切换页面时加载当前页面数据。你知道我该如何解决这个问题吗?

有一个简单的方法,就是把useEffect里面的列表API调用成compoenentDidMount,把所有的记录都设置到列表数组中。当您调用该页面时,您必须对列表进行切片。

例如 - 您必须每页显示 18 条记录

const [pages,setPages]=useState(0);
    const [currentPage,setCurrentPage]=useState(1);
    const [isLoading,setIsLoading]=useState(false);
    const [items,setItems]=useState([]);

    const itemPerPage=18;

    const currentPageHandler=page=>{
        setCurrentPage(page);
    }
 

useEffect(()=>{

        setIsLoading(true);

        axios.get('http://185.185.126.15:8080/management/onboarding/task/list')
          .then(res=>{
               setItems(res.data);
               setPageHandler(res.data.length);
               setIsLoading(false);
          }).catch(error=>{
              setIsLoading(false);
             console.log(error);
          });

    },[]);

const setPageHandler =length=>{
 
             const numOfItems=length;     
             let page = parseInt(numOfItems/itemPerPage);

             if(parseInt(numOfItems%18)!==0)
              page+=1;

              setPages(page);
   };

<React.Fragment>

    {items.slice((itemPerPage*(currentPage-1)),(itemPerPage*(currentPage)))
                   .map(item=><Records item={item} key={item.id} />)
            }

 { (items.length>=1) && <Pagination pages={pages} currentPageHandler={currentPageHandler}

</React.Fragment>

当您 select 分页组件中的当前页面然后将值作为参数传递给 CurrentPageHandler 方法

注意 - 如果您的平均数据量为 records <=10000,那么请使用此方法,但如果您必须检索大量数据超过 10000 那么最好的方法就是@dglozano 提到的。

@NafazBenzema 的建议可能会奏效,但您必须记住,根据他的建议,您将仅执行前端分页,这很可能不是您想要的想做。

前端分页意味着:

  1. 您将触发 仅一个请求 从后端获取所有元素。如果有 10.000.000 条记录,将立即传输所有 10.000.000 条记录(这取决于场景,可能需要您想象的相当长的时间)。
  2. 之后,当您在 table 中更改页面时将不会触发更多请求,因为您已经获取了所有记录。
  3. 您将使用前端逻辑(按照 Nafaz 的建议对数组进行切片)选择要在每个页面中显示的记录。

这种方法不一定是错误的,但也不理想,因为它不可扩展。此外,您似乎已经 后端分页了! 所以您应该尝试使用它

为此,您将:

  1. 只从后端获取第一页记录。例如,只有前 20 条记录...即使您总共有 10.000.000 条记录,也只有前 20 条会被 returned。
  2. 每个请求不仅要得到页面中的记录,还要得到一个数字,表示记录总数是多少。继续我之前的示例,该数字将是 10.000.000。 table 组件需要知道要请求的页面以及总共有多少页,而无需实际从后端获取所有记录。 它只需要知道总金额。我不确定你的 API 端点当前是否 return 这个总值,但在下面的代码示例中我会假设它确实存在并且它被称为 total (EDIT : 你的 API 做 return 这个值,它在响应中被称为 totalElements).
  3. 然后,每当您单击 table 分页 UI 以转到新页面或更改每页的记录数时,您将触发一个新请求只获取该新页面的元素,然后您必须用响应中收到的行“覆盖”组件状态中的前几行。为此,我们将当前 pagerowsPerPage 传递给 useEffect 依赖数组(第二个参数,在您的代码中是一个空数组 [])。这意味着每当 pagerowsPerPage 的值发生变化时,效果中的代码将再次成为 运行。

大致如下所示:

export default function BusinessCustomersTable() {
  const classes = useStyles();
  const [order, setOrder] = React.useState<Order>("asc");
  const [orderBy, setOrderBy] = React.useState<keyof Data>("businessName");
  const [selected, setSelected] = React.useState<number[]>([]);
  const [page, setPage] = React.useState(0);
  const [rowsPerPage, setRowsPerPage] = React.useState(5);
  const [rows, setRows] = useState<Data[]>([]);
  const [loading, setLoading] = useState(false);

  // TODO - move this to API file
  const apiUrl = "http://185.185.126.15:8080/api/management/onboarding/task";

  // Your API returns a property called `totalElements`.
  // We need to set pass it to the 'count' prop of the TablePagination component to do server side pagination.
  // Ref.: https://mui.com/api/table-pagination/
  const [totalRows, setTotalRows] = useState(0);
 
  useEffect(() => {
    axios.get(apiUrl, {
        params: { page, size: rowsPerPage },
      })
      .then((response) => {

          setRows(response.data.content);

          // As I said above, your response should return what's the total amount of rows.
          // In your API response, that value is called `totalElements`.
          setTotalRows(response.data.totalElements);

          setLoading(false);
      })
      .catch((error) => {
        console.error(error);
        setLoading(false);
      });


  }, [page, rowsPerPage]); // Whenever the current 'page' or the amount of 'rowsPerPage' change, your request will be fired again.

  const handleRequestSort = (
      event: React.MouseEvent<unknown>,
      property: keyof Data
  ) => {
    const isAsc = orderBy === property && order === "asc";
    setOrder(isAsc ? "desc" : "asc");
    setOrderBy(property);
  };

  const handleSelectAllClick = (
      event: React.ChangeEvent<HTMLInputElement>
  ) => {
    if (event.target.checked) {
      const newSelecteds = rows.map((n) => n.id);
      setSelected(newSelecteds);
      return;
    }
    setSelected([]);
  };

  const handleClick = (event: React.MouseEvent<unknown>, id: number) => {
    const selectedIndex = selected.indexOf(id);
    let newSelected: number[] = [];

    if (selectedIndex === -1) {
      newSelected = newSelected.concat(selected, id);
    } else if (selectedIndex === 0) {
      newSelected = newSelected.concat(selected.slice(1));
    } else if (selectedIndex === selected.length - 1) {
      newSelected = newSelected.concat(selected.slice(0, -1));
    } else if (selectedIndex > 0) {
      newSelected = newSelected.concat(
          selected.slice(0, selectedIndex),
          selected.slice(selectedIndex + 1)
      );
    }

    setSelected(newSelected);
  };

  const handleChangePage = (event: unknown, newPage: number) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (
      event: React.ChangeEvent<HTMLInputElement>
  ) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  const handleDeleteClick = async () => {
    // npm install qs
    var qs = require("qs");

    const response = await axios.delete(apiUrl, {
      params: {
        ids: selected,
      },
      paramsSerializer: (params) => {
        return qs.stringify(params);
      },
    });

    if (response.status === 204) {
      const updatedData = rows.filter(
          (row) => !selected.includes(row.id)
      ); // It'll return all data except selected ones

      setRows(updatedData); // reset rows to display in table.
    }
  };

  const isSelected = (id: number) => selected.indexOf(id) !== -1;

  const emptyRows =
      rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);

  return (
      <div className={classes.root}>
        <Paper className={classes.paper}>
          <EnhancedTableToolbar
              numSelected={selected.length}
              onClick={handleDeleteClick}
          />
          <TableContainer>
            <Table
                className={classes.table}
                aria-labelledby="tableTitle"
                aria-label="enhanced table"
            >
              <EnhancedTableHead
                  classes={classes}
                  numSelected={selected.length}
                  order={order}
                  orderBy={orderBy}
                  onSelectAllClick={handleSelectAllClick}
                  onRequestSort={handleRequestSort}
                  rowCount={rows.length}
              />
              <TableBody>
                {loading ? (
                    <div className="spinerr">
                      <CircularProgress />
                    </div>
                ) : null}
                {stableSort(rows, getComparator(order, orderBy))
                    .map((row, index) => {
                      const isItemSelected = isSelected(row.id);
                      const labelId = `enhanced-table-checkbox-${index}`;

                      return (
                          <TableRow
                              hover
                              onClick={(event) =>
                                  handleClick(event, row.id)
                              }
                              role="checkbox"
                              aria-checked={isItemSelected}
                              tabIndex={-1}
                              key={row.businessName}
                              selected={isItemSelected}
                          >
                            <TableCell padding="checkbox">
                              <Checkbox
                                  checked={isItemSelected}
                                  inputProps={{
                                    "aria-labelledby":
                                    labelId,
                                  }}
                              />
                            </TableCell>
                            <TableCell
                                component="th"
                                id={labelId}
                                scope="row"
                                padding="none"
                            >
                              {row.id}
                            </TableCell>
                            <TableCell align="right">
                              {row.businessName}
                            </TableCell>
                            <TableCell align="right">
                              {row.title}
                            </TableCell>
                            <TableCell align="right">
                              {row.status}
                            </TableCell>
                          </TableRow>
                      );
                    })}
                {emptyRows > 0 && (
                    <TableRow style={{ height: 53 * emptyRows }}>
                      <TableCell colSpan={6} />
                    </TableRow>
                )}
              </TableBody>
            </Table>
          </TableContainer>
          <TablePagination
              rowsPerPageOptions={[5, 10, 25]}
              component="div"
              count={totalRows} // This is what your request should be returning in addition to the current page of rows.
              rowsPerPage={rowsPerPage}
              page={page}
              onPageChange={handleChangePage}
              onRowsPerPageChange={handleChangeRowsPerPage}
          />
        </Paper>
      </div>
  );
}

需要考虑的其他无关事项:

  • 如果您在请求完成之前卸载组件(例如,如果您导航到页面中的其他位置),useEffect 的当前编写方式可能会给您带来一些错误。那是因为它将尝试设置未安装组件的状态(setLoadingsetRows)。为了防止这种情况,您可以遵循
  • 当您更改 rowsPerPage 时,您似乎也将页面重置为第一个。这将导致效果和请求被触发两次。您可以改为不将页面重置为第一个页面,或者将当前页面的状态和 rowsPerPage 保持在一个公共 useState 挂钩中,并始终同时更新两者。
  const classes = useStyles();
  const [order, setOrder] = React.useState<Order>("asc");
  const [orderBy, setOrderBy] = React.useState<keyof Data>("businessName");
  const [selected, setSelected] = React.useState<number[]>([]);
  const [page, setPage] = React.useState(0);
  const [rowsPerPage, setRowsPerPage] = React.useState(10);
  const [rows, setRows] = useState<Data[]>([]);
  const [loading, setLoading] = useState(false);
  const [totalElements, setData] = useState<any>({});

  

  // TODO - move this to API file
  const apiUrl = "http://185.185.126.15:8080/api/management/onboarding/task";


  useEffect(() => {

    const getData = async (page:number, size: number) => {
      let updatedState: Data[] = [];
  
      setLoading(true);

      const response = await axios.get(apiUrl, {
        params: { page, size },
      });
      setData(response.data.totalElements);
      setLoading(false);

      const objContent: any = response.data.content;

      for (let a = 0; a < objContent.length; a++) {
        updatedState[a] = createData(
          objContent[a].id,
          objContent[a].businessName,
          objContent[a].title,
          objContent[a].status
        );
      }
      setRows(updatedState);
    };
    getData(page, rowsPerPage);
  }, [page, rowsPerPage]);


  const handleRequestSort = (
    event: React.MouseEvent<unknown>,
    property: keyof Data
  ) => {
    const isAsc = orderBy === property && order === "asc";
    setOrder(isAsc ? "desc" : "asc");
    setOrderBy(property);
  };

  const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => {
    if (event.target.checked) {
      const newSelecteds = rows.map((n) => n.id);
      setSelected(newSelecteds);
      return;
    }
    setSelected([]);
  };

  const handleClick = (event: React.MouseEvent<unknown>, id: number) => {
    const selectedIndex = selected.indexOf(id);
    let newSelected: number[] = [];

    if (selectedIndex === -1) {
      newSelected = newSelected.concat(selected, id);
    } else if (selectedIndex === 0) {
      newSelected = newSelected.concat(selected.slice(1));
    } else if (selectedIndex === selected.length - 1) {
      newSelected = newSelected.concat(selected.slice(0, -1));
    } else if (selectedIndex > 0) {
      newSelected = newSelected.concat(
        selected.slice(0, selectedIndex),
        selected.slice(selectedIndex + 1)
      );
    }

    setSelected(newSelected);
  };

  const handleChangePage = (event: unknown, newPage: number) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  const handleDeleteClick = async () => {
    // npm install qs
    var qs = require("qs");

    const response = await axios.delete(apiUrl, {
      params: {
        ids: selected,
      },
      paramsSerializer: (params) => {
        return qs.stringify(params);
      },
    });

    if (response.status === 204) {
      const updatedData = rows.filter((row) => !selected.includes(row.id)); // It'll return all data except selected ones

      setRows(updatedData); // reset rows to display in table.
    }
  };

  const isSelected = (id: number) => selected.indexOf(id) !== -1;

  const emptyRows =
    page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0;

  return (
    <div className={classes.root}>
      <Paper className={classes.paper}>
        <EnhancedTableToolbar
          numSelected={selected.length}
          onClick={handleDeleteClick}
        />
        <TableContainer>
          <Table
            className={classes.table}
            aria-labelledby='tableTitle'
            aria-label='enhanced table'
          >
            <EnhancedTableHead
              classes={classes}
              numSelected={selected.length}
              order={order}
              orderBy={orderBy}
              onSelectAllClick={handleSelectAllClick}
              onRequestSort={handleRequestSort}
              rowCount={rows.length}
            />
            <TableBody>
              {loading ? (
                <div className='spinerr'>
                  <CircularProgress />
                </div>
              ) : null}
              {stableSort(rows, getComparator(order, orderBy))
                .map((row, index) => {
                  const isItemSelected = isSelected(row.id);
                  const labelId = `enhanced-table-checkbox-${index}`;
                 
                  return (
                    <TableRow
                      hover
                      onClick={(event) => handleClick(event, row.id)}
                      role='checkbox'
                      aria-checked={isItemSelected}
                      tabIndex={-1}
                      key={row.businessName}
                      selected={isItemSelected}
                    >
                      <TableCell padding='checkbox'>
                        <Checkbox
                          checked={isItemSelected}
                          inputProps={{
                            "aria-labelledby": labelId,
                          }}
                        />
                      </TableCell>
                      <TableCell
                        component='th'
                        id={labelId}
                        scope='row'
                        padding='none'
                      >
                        {row.id}
                      </TableCell>
                      <TableCell align='right'>{row.businessName}</TableCell>
                      <TableCell align='right'>{row.title}</TableCell>
                      <TableCell align='right'>{row.status}</TableCell>
                    </TableRow>
                  );
                })}
              {/* {emptyRows > 0 && (
                <TableRow style={{ height: 53 * emptyRows }}>
                  <TableCell colSpan={6} />
                </TableRow>
              )} */}
            </TableBody>
          </Table>
        </TableContainer>
        <TablePagination
          rowsPerPageOptions={[5, 10, 25]}
          component='div'
          count={totalElements}
          rowsPerPage={rowsPerPage}
          page={page}
          onPageChange={handleChangePage}
          onRowsPerPageChange={handleChangeRowsPerPage}
        />
      </Paper>
    </div>
  );
}