reactjs - react-data-table-component onChangePage 方法在 onSelectedRowsChange 方法后触发

标签 reactjs react-data-table-component

我正在尝试按页跟踪所选项目。

  • 当我点击下一页时,我将使用数据表的“onChangePage”事件更新“currentPage”状态。
  • 由于“onChangeRowsPerPage”事件先于“onChangePage”事件触发,并在页面更改之前重置所选项目,所以我最终总是会丢失上一页所选项目。

这里是codesandbox链接:https://codesandbox.io/s/affectionate-forest-tx3309?file=/src/App.js

无论我尝试什么,当我单击下一页时,它都会首先触发“onChangeRowsPerPage”并删除所有选定的项目。关于如何解决这个问题有什么想法吗?

谢谢!

import { useEffect, useMemo, useState } from "react";
import axios from "axios";
import DataTable from "react-data-table-component";
import "./styles.css";

export default function App() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [currentPage, setCurrentPage] = useState(1);
const [selectedRows, setSelectedRows] = useState([]);

const fetchUsers = async (page) => {
  setLoading(true);
  const response = await axios.get(`https://reqres.in/api/users?page=${page}&per_page=${perPage}&delay=1`);
  setData(response.data.data);
  setTotalRows(response.data.total);
  setLoading(false);
};

const handlePageChange = (page) => {
  setCurrentPage(page);
  fetchUsers(page);
};

const handlePerRowsChange = async (newPerPage, page) => {
  setLoading(true);

  const response = await axios.get(`https://reqres.in/api/users?page=${page}&per_page=${newPerPage}&delay=1`);

  setData(response.data.data);
  setPerPage(newPerPage);
  setLoading(false);
};

const handleRowSelected = async (row) => {
  console.log(row, currentPage);

  // Check current page has selected items
  const selectedPageRowIndex = selectedRows.findIndex((row) => row.page === currentPage);

  // If there is no selected records for this page
  if (selectedPageRowIndex === -1) {
    const tmpRow = { ...row }; // Copy returned row object
    tmpRow.page = currentPage; // Set page
    setSelectedRows([...selectedRows, tmpRow]); // Update state
  } else {
    // If exist, update

    console.log("Current page :", currentPage, " updating...");

    const tmpSelectedPageRows = [...selectedRows]; // Copy state

    tmpSelectedPageRows[selectedPageRowIndex].selectedRows = row.selectedRows; // Update selected rows

    setSelectedRows(tmpSelectedPageRows); // Update state
  }
};

useEffect(() => {
  fetchUsers(1); // fetch page 1 of users
}, []); // eslint-disable-line react-hooks/exhaustive-deps

// Table Column Configuration
const columns = useMemo(() => [
  {
    name: "Avatar",
    cell: (row) => (
      <img height="30px" width="30px" alt={row.first_name} src={row.avatar} />
  )
  },
  {
    name: "First Name",
    selector: (row) => row.first_name
  },
  {
    name: "Last Name",
    cell: (row) => row.last_name
  },
  {
    name: "Email",
    selector: (row) => row.email
  }
]);

return (
  <div className="App">
    <h1>react-data-table-component</h1>
    <p>with remote pagination + pre/selected rows</p>

    {JSON.stringify(selectedRows, null, 2)}

    <DataTable
      title="Users"
      columns={columns}
      data={data}
      progressPending={loading}
      pagination
      paginationServer
      paginationTotalRows={totalRows}
      onChangeRowsPerPage={handlePerRowsChange}
      onChangePage={handlePageChange}
      selectableRows
      onSelectedRowsChange={handleRowSelected}
    />
  </div>
);
}

最佳答案

我必须进入繁重的解决方法模式才能做到这一点,该组件不能很好地支持具有可选行的服务器端分页。

看一下:

https://codesandbox.io/s/flamboyant-tesla-22fbq7?file=/src/App.js

import { useCallback, useEffect, useMemo, useState } from "react";
import axios from "axios";
import DataTable from "react-data-table-component";

export default function App() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [totalRows, setTotalRows] = useState(10);
  const [action] = useState({ fromUser: false }); //this is a way to have an instant-changing state
  const [rowsPerPage, setRowsPerPage] = useState(4); //change to 10 after you remove paginationRowsPerPageOptions
  const [currentPage, setCurrentPage] = useState(1);
  const [selectedRowsPerPage, setSelectedRowsPerPage] = useState([]);

  const fetchUsers = async (page, rowsPerPage) => {
    setLoading(true);
    const response = await axios.get(
      `https://reqres.in/api/users?page=${page}&per_page=${rowsPerPage}&delay=1`
    );
    setData(response.data.data);
    setTotalRows(response.data.total);
    setLoading(false);
  };

  const handlePageChange = (page) => {
    fetchUsers(page, rowsPerPage);
    setCurrentPage(page);
  };

  const handleRowsPerPageChange = async (newRowsPerPage) => {
    if (!data.length) return; //when the table is rendered for the first time, this would trigger, and we don't need to call fetchUsers again
    fetchUsers(1, newRowsPerPage);
    setRowsPerPage(newRowsPerPage);
    setCurrentPage(1);
    setSelectedRowsPerPage([]);
  };

  const handleOnSelectedRowsChange = useCallback(
    ({ selectedRows }) => {
      if (!action.fromUser) return; //the component always trigger this with 0 selected rows when it renders a page, what would clear the selection

      selectedRowsPerPage[currentPage] = selectedRows; //there is no way to tell if a row was DEselected, so I had to control the selected rows per page,
      //the array would get an index to control each page
      console.log(JSON.stringify(selectedRowsPerPage));
    },
    [currentPage, selectedRowsPerPage, action.fromUser]
  );

  const handleMouseEnter = () => {
    action.fromUser = true; //this was the way I found to prevent the component to clear the selection on every page render,
    //if the user is not with the mouse on a row, doesn't allow to change the selected rows
  };

  const handleMouseLeave = () => {
    action.fromUser = false; //When the users moves the mouse out of a row, block the changes to the selected rows array (line 39)
  };

  const getAllSelectedRows = () => {
    //if you need to get all of the selected rows in all pages, call this
    const allSelected = [];
    selectedRowsPerPage.forEach((selectedPerPage) => {
      if (selectedPerPage) {
        selectedPerPage.forEach((selectRow) => {
          allSelected.push(selectRow);
        });
      }
    });
    return allSelected;
  };

  //this applies the selected rows on the page renders, it checks if the id of the row exists in the array
  const handleApplySelectedRows = (row) =>
    selectedRowsPerPage[currentPage]?.filter(
      (selectedRow) => selectedRow.id === row.id
    ).length > 0;

  useEffect(() => {
    //it's controlled by page, for example selectedRowsPerPage[1] contains the selected rows for page 1 and so forth...
    const preSelectedItems = [
      //index 0 is always null, because pages start at 1
      null,

      //index 1 in the selected for fir the first page:
      [
        {
          id: 3,
          email: "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="53363e3e327d243c3d34132136222136207d3a3d" rel="noreferrer noopener nofollow">[email protected]</a>",
          first_name: "Emma",
          last_name: "Wong",
          avatar: "https://reqres.in/img/faces/3-image.jpg"
        },
        {
          id: 1,
          email: "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="94f3f1fbe6f3f1baf6f8e1e0fcd4e6f1e5e6f1e7bafdfa" rel="noreferrer noopener nofollow">[email protected]</a>",
          first_name: "George",
          last_name: "Bluth",
          avatar: "https://reqres.in/img/faces/1-image.jpg"
        }
      ],

      //index 2 null for example, because nothing is selected for page 2
      null,

      //index 3, one selected for page 3
      [
        {
          id: 11,
          email: "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="aec9cbc1dcc9cb80cbcad9cfdccaddeedccbdfdccbdd80c7c0" rel="noreferrer noopener nofollow">[email protected]</a>",
          first_name: "George",
          last_name: "Edwards",
          avatar: "https://reqres.in/img/faces/11-image.jpg"
        }
      ]
    ];

    setSelectedRowsPerPage(preSelectedItems);
  }, []);

  const columns = useMemo(
    () => [
      {
        name: "Avatar",
        cell: (row) => (
          <img
            height="30px"
            width="30px"
            alt={row.first_name}
            src={row.avatar}
          />
        )
      },
      {
        name: "First Name",
        selector: (row) => row.first_name
      },
      {
        name: "Last Name",
        cell: (row) => row.last_name
      },
      {
        name: "Email",
        selector: (row) => row.email
      }
    ],
    []
  );

  return (
    <div className="App">
      <h1>react-data-table-component</h1>

      <h2>Users</h2>
      {
        //had to remove the title in the DataTable, because the select count was only regarging the current page not all selected rows
      }

      <DataTable
        pagination
        paginationServer
        selectableRows
        columns={columns}
        data={data}
        progressPending={loading}
        paginationTotalRows={totalRows}
        selectableRowsNoSelectAll={true} //I needed to remove the select all, because it would not work due to the mouse enter/leave workaround
        paginationDefaultPage={currentPage}
        paginationRowsPerPageOptions={[4, 8, 15]} //you can remove it later, just to have more pages
        paginationPerPage={rowsPerPage}
        onRowMouseEnter={handleMouseEnter}
        onRowMouseLeave={handleMouseLeave}
        onChangePage={handlePageChange}
        onChangeRowsPerPage={handleRowsPerPageChange}
        onSelectedRowsChange={handleOnSelectedRowsChange}
        selectableRowSelected={handleApplySelectedRows}
      />

      <button
        onClick={() => {
          setSelectedRowsPerPage([...selectedRowsPerPage]);
        }}
      >
        Refresh All Selected
      </button>
      <br />
      <br />
      {JSON.stringify(getAllSelectedRows())}
    </div>
  );
}

关于reactjs - react-data-table-component onChangePage 方法在 onSelectedRowsChange 方法后触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74800035/

相关文章:

reactjs - 在react-konva上动态渲染数据库中的图像

javascript - 如何获取芯片名称或芯片索引?

reactjs - 如何验证Yup的开始日期是否晚于结束日期?

reactjs - Flatlist onEndReached 无限循环

reactjs - 如何在React-data-table中设置paginationRowsPerPageOptions ['ALL' ,'50' ,'' 100']

reactjs - 如何使用 useRef 和 typescript 将函数分配给 ref?

reactjs - React 数据表可扩展行 : how to pass additional Props

javascript - 错误: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined

reactjs - 样式化组件错误 "it looks like an unknown prop "响应“已发送到 DOM,这可能会触发 React 控制台错误。”