Rumah > Soal Jawab > teks badan
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.
P粉5949413012024-04-02 13:20:08
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.
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); } }
Alihkan roleMenus
kemas kini status ke dalam kitaran hayat komponen.
Cara mudah ialah menggunakan cangkuk useEffect
untuk menyegerakkan keadaan useEffect
挂钩将 roleMenus
状态同步到当前 role.menus
kepada nilai role.menus
semasa.
const role = useSelector((state: RootState) => state.roles.selected) as Role; const [roleMenus, setRoleMenus] = useState<RoleMenuItem[]>([]); useEffect(() => { if (role?.menus) { try { const parsedMenus = JSON.parse(role.menus) as RoleMenuItem[]; setRoleMenus(parsedMenus); } catch (error) { console.error("Error parsing role menus:", error); } } }, [role?.menus]);
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 nilai role.menus
semasa. Anda harus ingat bahawa jika anda mendapati diri anda menulis gandingan useState
/useEffect
, maka kira-kira 100% masa anda harus menggunakan useMemo
cangkuk ganti.
const role = useSelector((state: RootState) => state.roles.selected) as Role; const roleMenus = useMemo<RoleMenuItem[]>(() => { try { return JSON.parse(role.menus) as RoleMenuItem[]; } catch (error) { console.error("Error parsing role menus:", error); return []; } }, [role?.menus]);
Jika ini adalah sesuatu yang anda kerap pilih dan kira daripada Redux, saya syorkan anda mempertimbangkan untuk memindahkan logik ke dalam fungsi pemilih.
Contoh:
const selectRoleMenus = (state: RootState) => { const role = state.roles.selected; try { return JSON.parse(role.menus) as RoleMenuItem[]; } catch (error) { console.error("Error parsing role menus:", error); return []; } };
const role = useSelector((state: RootState) => state.roles.selected) as Role; const roleMenus = useSelector(selectRoleMenus) as RoleMenuItem[];;
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.