Bạn đang gặp vấn đề với việc **bảo toàn trạng thái** của các bảng dữ liệu (`DataGrid`) trong React khi sử dụng Material UI sau mỗi lần tải lại trang? Bài viết này sẽ cung cấp một hướng dẫn chi tiết từng bước để giúp bạn giải quyết vấn đề này, đảm bảo trải nghiệm người dùng liền mạch và nhất quán. Chúng ta sẽ đi sâu vào các kỹ thuật **quản lý trạng thái**, sử dụng `localStorage` và khắc phục các lỗi thường gặp liên quan đến `DataGrid` của Material UI.
Trong nhiều ứng dụng React, việc duy trì trạng thái của các component sau khi người dùng tải lại trang là rất quan trọng. Đặc biệt, với các bảng dữ liệu phức tạp như `DataGrid` của Material UI, việc mất trạng thái (ví dụ: lựa chọn hàng, sắp xếp, bộ lọc) có thể gây khó chịu cho người dùng. Vấn đề thường gặp là khi có hai `DataGrid` tương tác với nhau (ví dụ: một bảng hiển thị tất cả dữ liệu và một bảng hiển thị các mục "yêu thích"), việc bảo toàn trạng thái trở nên phức tạp hơn.
Mục tiêu của chúng ta là xây dựng một hệ thống, nơi người dùng có thể chọn các hàng trong một `DataGrid` (coi như là "yêu thích"), và các hàng này sẽ tự động xuất hiện trong một `DataGrid` khác. Đồng thời, trạng thái lựa chọn này phải được lưu lại, sao cho khi người dùng tải lại trang, các hàng đã chọn vẫn được giữ nguyên ở cả hai bảng. Chúng ta sẽ sử dụng `localStorage` để lưu trữ thông tin này.
Chúng ta sẽ sử dụng `localStorage` để lưu trữ ID của các hàng được chọn. Hàm `useEffect` sẽ được sử dụng để tải trạng thái từ `localStorage` khi component được mount và để cập nhật `localStorage` mỗi khi trạng thái lựa chọn thay đổi. Dưới đây là một ví dụ code minh họa:
import { Box } from "@mui/material";
import { DataGrid, GridColDef, GridRowSelectionModel, GridRowsProp } from "@mui/x-data-grid";
import React, { useEffect, useState } from "react";
import { People } from "../../data/people";
import { LocalStorageTypes, Person } from "../../models";
import styles from "./styles/SaveFavorites.module.scss";
interface PeopleTablesProps {}
const allPeople: Person[] = People;
// Set up for Data Drid
const rows: GridRowsProp = allPeople;
const columns: GridColDef[] = [
{ field: "first_name", headerName: "First Name", width: 150, flex: 1, minWidth: 150 },
{ field: "last_name", headerName: "Last Name", width: 100, flex: 1, minWidth: 100 },
{ field: "gender", headerName: "Gender", width: 150, flex: 1, minWidth: 150 },
{ field: "country", headerName: "Country", width: 150, flex: 1, minWidth: 150 },
];
const PeopleTables: React.FC<PeopleTablesProps> = () => {
// Get Selected Rows from Local Storage
const storedFavorites = JSON.parse(localStorage.getItem(LocalStorageTypes.FAVORITES) || "[]");
// Save id from selected Row
const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>(storedFavorites);
const [selectedPeople, setSelectedPeople] = useState<Person[]>([]);
// Add selected rows ID to localStorage
if (rowSelectionModel !== undefined) {
localStorage.setItem(LocalStorageTypes.FAVORITES, JSON.stringify(rowSelectionModel));
}
useEffect(() => {
// Update selectedPeople
setSelectedPeople(
rowSelectionModel.map((selectedId) => {
const row = rows.find((item) => item.id === selectedId) as Person;
const { id, ...rest } = row;
return { id, ...rest };
})
);
}, [rowSelectionModel, rows]);
return (
<>
<h2>All the people</h2>
<div style={{ height: 300, width: "100%" }}>
<Box
sx={{
height: "400px",
width: "100%",
"& .MuiDataGrid-columnHeaders": {
backgroundColor: "#A9A9A9",
},
}}
>
<DataGrid
checkboxSelection
onRowSelectionModelChange={(newRowSelectionModel) => {
setRowSelectionModel(newRowSelectionModel);
}}
rowSelectionModel={rowSelectionModel}
initialState={{
pagination: {
paginationModel: {
pageSize: 10,
},
},
}}
pageSizeOptions={[5, 10, 25]}
rows={rows}
columns={columns}
className={styles.savefavorites}
sx={{
"& .MuiDataGrid-columnHeaderCheckbox .MuiDataGrid-columnHeaderTitleContainer": {
display: "none",
},
}}
/>
</Box>
<h2>Favourite people</h2>
<Box
sx={{
height: "400px",
width: "100%",
"& .MuiDataGrid-columnHeaders": {
backgroundColor: "#A9A9A9",
},
}}
>
<DataGrid
checkboxSelection
onRowSelectionModelChange={(newRowSelectionModel) => {
setRowSelectionModel(newRowSelectionModel);
}}
rowSelectionModel={rowSelectionModel}
initialState={{
pagination: {
paginationModel: {
pageSize: 10,
},
},
}}
pageSizeOptions={[5, 10, 25]}
rows={selectedPeople}
columns={columns}
className={styles.savefavorites}
sx={{
"& .MuiDataGrid-columnHeaderCheckbox .MuiDataGrid-columnHeaderTitleContainer": {
display: "none",
},
}}
/>
</Box>
</div>
</>
);
};
export default PeopleTables;
Giải thích code:
Lỗi "Cannot update a component..." thường xảy ra khi bạn cố gắng cập nhật state của một component trong quá trình render một component khác. Trong trường hợp này, lỗi có thể do việc cập nhật `selectedPeople` trong quá trình render `DataGrid`. Để khắc phục, hãy đảm bảo rằng việc cập nhật state chỉ xảy ra bên trong `useEffect` hoặc các event handlers (ví dụ: `onRowSelectionModelChange`).
Thuộc tính `keepNonExistentRowsSelected` trong `DataGrid` có thể giúp giải quyết vấn đề này. Theo tài liệu của MUI, nó cho phép `DataGrid` giữ lại các hàng đã chọn ngay cả khi chúng không còn tồn tại trong dữ liệu hiện tại. Điều này đặc biệt hữu ích khi làm việc với dữ liệu phân trang từ server. Thêm thuộc tính này vào cấu hình `DataGrid` của bạn:
<DataGrid
checkboxSelection
onRowSelectionModelChange={(newRowSelectionModel) => {
setRowSelectionModel(newRowSelectionModel);
}}
rowSelectionModel={rowSelectionModel}
initialState={{
pagination: {
paginationModel: {
pageSize: 10,
},
},
}}
pageSizeOptions={[5, 10, 25]}
rows={rows}
columns={columns}
className={styles.savefavorites}
sx={{
"& .MuiDataGrid-columnHeaderCheckbox .MuiDataGrid-columnHeaderTitleContainer":
{
display: "none",
},
}}
keepNonExistentRowsSelected
/>
Ngoài `localStorage`, bạn có thể cân nhắc sử dụng các thư viện quản lý trạng thái như Redux, Zustand hoặc Recoil để quản lý trạng thái phức tạp của `DataGrid`. Các thư viện này cung cấp các công cụ mạnh mẽ để quản lý và chia sẻ trạng thái giữa các components, giúp ứng dụng của bạn dễ bảo trì và mở rộng hơn.
Việc bảo toàn trạng thái của `DataGrid` trong React (Material UI) sau khi tải lại trang đòi hỏi sự kết hợp giữa việc sử dụng `localStorage`, `useEffect` và hiểu rõ về cách Material UI hoạt động. Bằng cách áp dụng các kỹ thuật và giải pháp được trình bày trong bài viết này, bạn có thể tạo ra trải nghiệm người dùng tốt hơn và ứng dụng của bạn trở nên chuyên nghiệp hơn. Đừng quên, việc kiểm tra và gỡ lỗi kỹ lưỡng là rất quan trọng để đảm bảo rằng giải pháp của bạn hoạt động ổn định và hiệu quả.
Bài viết liên quan