我正在尝试按页跟踪所选项目。
- 当我点击下一页时,我将使用数据表的“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/