Saya menggunakan React TypeScript, kit alat Redux dan UI Bahan. Saya mendapat ralat ini apabila memanggil API:
Ralat: Terlalu banyak paparan semula. React mengehadkan bilangan pemaparan untuk mengelakkan gelung tak terhingga. dalam renderWithHooks (http://127.0.0.1:5173/node_modules/.vite/deps/chunk-QJV3R4PZ.js?v=8a99eba5:12178:23) Dalam mountInminatedComponent (http://127.0.0.1:5173/node_modules/.vite/deps/chunk-QJV3R4PZ.js?v=8a99eba5:14921:21) Pada beginWork (http://127.0.0.1:5173/node_modules/.vite/deps/chunk-QJV3R4PZ.js?v=8a99eba5:15902:22)....
Saya berikan kod saya di bawah:
EditMenuPermission.tsx
//EditMenuPermission.tsx //other imports /* ++++ Redux Imports ++++ */ import { useDispatch, useSelector } from "react-redux"; import { AppDispatch, RootState } from "src/redux"; import { roleActions } from "../roles/RolesActions"; /* ---- Redux Imports ---- */ const EditMenuPermission = () => { const { id } = useParams(); const [selected, setSelected] = useState<RoleMenuItem[]>( [] as RoleMenuItem[] ); const [selectedIds, setSelectedIds] = useState<number[]>([] as number[]); const role = useSelector((state: RootState) => state.roles.selected) as Role; const [roleMenus, setRoleMenus] = useState<RoleMenuItem[]>([]); if (role?.menus) { try { const parsedMenus = JSON.parse(role.menus) as RoleMenuItem[]; setRoleMenus(parsedMenus); } catch (error) { console.error("Error parsing role menus:", error); } } const dispatch = useDispatch<AppDispatch>(); useEffect(() => { dispatch(roleActions.findOne(id as unknown as number)); }, [dispatch, id, role?.id]); console.log("previousMenus:", roleMenus, "selected:", selected); const handleCreatePayload = async () => { const updatedMenus = [...roleMenus]; selected.forEach((selectedItem) => { const existingItemIndex = updatedMenus.findIndex( (menu) => menu.id === selectedItem.id ); if (existingItemIndex !== -1) { updatedMenus[existingItemIndex] = selectedItem; } else { updatedMenus.push(selectedItem); } }); setRoleMenus(updatedMenus); const payload = { name: role.name, is_active: true, is_deleted: false, menus: JSON.stringify(updatedMenus), }; console.log("updated Menus:", updatedMenus); const updateRole = await dispatch(roleActions.update(role.id, payload)); console.log(updateRole); }; return ( <Box> <AdminTitleContainer> <AdminTitle variant="h5">Role Permission</AdminTitle> </AdminTitleContainer> <Grid container spacing={2}> <Grid item xs={9}> <Box> <RoleMenuTrees selected={selected} setSelected={setSelected} selectedIds={selectedIds} setSelectedIds={setSelectedIds} roleMenus={roleMenus} /> </Box> </Grid> <Grid item xs={3}> <Button variant="contained" color="primary" startIcon={<AddCircle />} onClick={handleCreatePayload} sx={{ position: "fixed" }} > Save </Button> </Grid> </Grid> </Box> ); }; export default EditMenuPermission;
RoleMenuTrees.tsx
//other imports /* ++++ Redux Imports ++++ */ import { useDispatch, useSelector } from "react-redux"; import { AppDispatch, RootState } from "src/redux"; import { roleActions } from "src/features/admin/roles/RolesActions"; /* ---- Redux Imports ---- */ import { useEffect } from "react"; import { useParams } from "react-router-dom"; import { useRoleMenuTree } from "src/hooks/useMenuTree"; import { SingleRoleMenuDTO } from "src/features/admin/roles/RolesDTO"; import { menuActions } from "src/features/admin/menu/MenuActions"; import { AllMenu, Permission, PermissionType, RoleMenuItem, SingleRole, } from "../../RoleDTO"; type RoleMenuTreesProp = { selected: RoleMenuItem[]; setSelected: React.Dispatch<React.SetStateAction<RoleMenuItem[]>>; selectedIds: number[]; setSelectedIds: React.Dispatch<React.SetStateAction<number[]>>; roleMenus: RoleMenuItem[]; }; const RoleMenuTrees = ({ selected, setSelected, selectedIds, setSelectedIds, roleMenus, }: RoleMenuTreesProp) => { const dispatch = useDispatch<AppDispatch>(); const { id } = useParams(); const roleMenusJSON = useSelector( (state: RootState) => state.roles.selected as SingleRole )?.menus; const allMenus = useSelector( (state: RootState) => state.menus.list ) as AllMenu[]; useEffect(() => { dispatch(menuActions.getList()); }, [dispatch, id]); /*++++ merging roleMenus + allMenus starts +++++*/ const mergedMenus = allMenus?.map((menu) => { const matchingMenu = roleMenus.find( (roleMenu: RoleMenuItem) => roleMenu.id === menu.id ); if (matchingMenu) { const { permissions: _, ...rest } = { ...menu, ...matchingMenu }; return rest; } else { const permissions = JSON.parse(menu.permissions) as Permission[]; const permissionType = {} as PermissionType; permissions?.forEach((permission) => { const { key } = permission; permissionType[key] = false; }); const { permissions: _, ...rest } = { ...menu, permission_type: permissions, ...permissionType, }; return rest; } }); console.log("mergedMenus:", mergedMenus); /*---- merging roleMenus + allMenus ends ----*/ const createRoleMenuTree = useRoleMenuTree( mergedMenus as unknown as SingleRoleMenuDTO[] ); const tree = createRoleMenuTree.tree; const mapMenu = createRoleMenuTree.mapMenu; return ( <Box> <Box sx={{ backgroundColor: "#fafafa" }}> {/*++++ Menu List starts ++++*/} <TreeView className="TreeView" defaultExpandIcon={ <ChevronRightIcon sx={{ fontSize: "1.5rem !important" }} /> } defaultCollapseIcon={ <ExpandMoreIcon sx={{ fontSize: "1.5rem !important" }} /> } > {tree?.map((data) => ( <Box key={data.id}> <RoleMenuTree data={data as unknown as RoleMenuItem} selected={selected} setSelected={setSelected} selectedIds={selectedIds} setSelectedIds={setSelectedIds} mapMenu={mapMenu} /> </Box> ))} </TreeView> {/*---- Menu List ends ----*/} </Box> </Box> ); }; export default RoleMenuTrees;
Saya cuba mengalih keluar kebergantungan dalam useEffect. Tetapi kesilapan itu masih wujud.
Soalan
Masalahnya di sini ialah beratur kemas kini keadaan React di luar kitaran hayat komponen React merupakan kesan sampingan yang tidak disengajakan. Apabila
EditMenuPermission
组件呈现时都会调用此代码,如果role.menus
adalah benar, kemas kini keadaan akan dimasukkan ke dalam baris gilir dan komponen dicetuskan untuk dipaparkan semula. Ini ialah gelung render yang anda lihat.Penyelesaian
Alihkan
roleMenus
kemas kini status ke dalam kitaran hayat komponen.Penyelesaian mudah
Cara mudah ialah menggunakan cangkuk
useEffect
untuk menyegerakkan keadaanuseEffect
挂钩将roleMenus
状态同步到当前role.menus
kepada nilairole.menus
semasa.Penyelesaian yang lebih baik 1
Ini berfungsi, tetapi secara amnya dianggap sebagai anti-corak React untuk menyimpan "keadaan" terbitan ke dalam keadaan React. Nilai
roleMenus
值可以很容易地从当前的role.menus
值计算出来。您应该记住,如果您发现自己编写了useState
/useEffect
耦合,那么大约 100% 的情况下,您应该使用useMemo
semasa boleh dikira dengan mudah daripada nilairole.menus
semasa. Anda harus ingat bahawa jika anda mendapati diri anda menulis gandinganuseState
/useEffect
, maka kira-kira 100% masa anda harus menggunakanuseMemo
cangkuk ganti.Penyelesaian yang lebih baik 2
Jika ini adalah sesuatu yang anda kerap pilih dan kira daripada Redux, saya syorkan anda mempertimbangkan untuk memindahkan logik ke dalam fungsi pemilih.
Contoh:
Cadangan untuk penambahbaikan selanjutnya
Lebih baik lagi, apabila mengemas kini keadaan Redux, hanya JSON.parse data peranan dalam fungsi pengurangan kepingan, jadi anda hanya perlu melakukan satu pengiraan setiap kali keadaan dikemas kini, bukannya setiap kali keadaan dibaca.