Preface
Recently, I used react18 + ts to route. I used v6 when I needed to load the routing table dynamically according to permissions. I encountered many problems.
Compared with v5, mapping through routing tables is a good change (personally think), but how to dynamically load routing tables based on permissions? I also found a lot of information on the website and found that most of them were dynamic routes from previous versions. If I wrote them according to the current routing table, it would definitely not work. Could it be that it is as complicated as the old version? I can only write one by myself. If there is a better way, I hope the big guys will give me some advice.
Ideas
The general idea is: first, only configure the default route in the routing table, such as login page and 404 page. After waiting for the user to log in successfully, get the user permission list and navigation list, write a tool function to recursively call to get the routing table, map it into components according to the keywords, and finally return to get the new routing table.
The process is as follows
- User login successfully
- Get user permission list
- Get the user navigation menu list
- Generate routing tables based on permissions and navigation
It’s always shallow to talk about it on paper, let’s take a look at it in reality.
Implement dynamic routing
router/ default route
import { lazy } from "react"; import { Navigate } from "react-router-dom"; // React component lazy loading// Quick import tool functionsconst lazyLoad = (moduleName: string) => { const Module = lazy(() => import(`views/${moduleName}`)); return <Module />; }; //Routing authentication componentconst Appraisal = ({ children }: any) => { const token = ("token"); return token ? children : <Navigate to="/login" />; }; interface Router { name?: string; path: string; children?: Array<Router>; element: any; } const routes: Array<Router> = [ { path: "/login", element: lazyLoad("login"), }, { path: "/", element: <Appraisal>{lazyLoad("sand-box")}</Appraisal>, children: [ { path: "", element: <Navigate to="home" />, }, { path: "*", element: lazyLoad("sand-box/nopermission"), }, ], }, { path: "*", element: lazyLoad("not-found"), }, ]; export default routes;
redux login/
Pay attention to the logo with //import!Each time the navigation list is updated, the route update action is triggered.
HandelFilterRouter is to obtain the routing table based on the navigation menu list and permission list
import { INITSIDEMENUS, UPDATUSERS, LOGINOUT, UPDATROUTES } from "./contant"; import { getSideMenus } from "services/home"; import { loginUser } from "services/login"; import { patchRights } from "services/right-list"; import { handleSideMenu } from "@/utils/devUtils"; import { handelFilterRouter } from "@/utils/routersFilter"; import { message } from "antd"; // Get the navigation menu listexport const getSideMenusAction = (): any => { return (dispatch: any, state: any) => { getSideMenus().then((res: any) => { const rights = state().; const newMenus = handleSideMenu(res, rights); dispatch({ type: INITSIDEMENUS, menus: newMenus }); dispatch(updateRoutesAction()); //import! }); }; }; // Log outexport const loginOutAction = (): any => ({ type: LOGINOUT }); // Update navigation menuexport const updateMenusAction = (item: any): any => { return (dispatch: any) => { patchRights(item).then((res: any) => { dispatch(getSideMenusAction()); }); }; }; //Routing update //import!export const updateRoutesAction = (): any => { return (dispatch: any, state: any) => { const rights = state().; const menus = state().; const routes = handelFilterRouter(rights, menus); //import! dispatch({ type: UPDATROUTES, routes }); }; }; // Log inexport const loginUserAction = (item: any, navigate: any): any => { return (dispatch: any) => { loginUser(item).then((res: any) => { if ( === 0) { ("Error in username or password"); } else { ("token", res[0].username); dispatch({ type: UPDATUSERS, users: res[0] }); dispatch(getSideMenusAction()); navigate("/home"); } }); }; };
Utils tool function processing
Let me tell you why I want to map element to the corresponding component operation. The reason is that I use redux-persist (redux persistence). Those who are not familiar with this plug-in can read my article:redux-persistIf it is directly converted and saved to local and then taken out to render, it will be problematic. Therefore, it is necessary to save the element as a mapping path first, and then map the corresponding components again before rendering.
The data return format of each background is different, and you need to convert it yourself. My conversion here is for reference only. ps:defaulyRoutes is the same as default router/export. You can make a small optimization and reuse it.
import { lazy } from "react"; import { Navigate } from "react-router-dom"; // Quick import tool functionsconst lazyLoad = (moduleName: string) => { const Module = lazy(() => import(`views/${moduleName}`)); return <Module />; }; const Appraisal = ({ children }: any) => { const token = ("token"); return token ? children : <Navigate to="/login" />; }; const defaulyRoutes: any = [ { path: "/login", element: lazyLoad("login"), }, { path: "/", element: <Appraisal>{lazyLoad("sand-box")}</Appraisal>, children: [ { path: "", element: <Navigate to="home" />, }, { path: "*", element: lazyLoad("sand-box/nopermission"), }, ], }, { path: "*", element: lazyLoad("not-found"), }, ]; // Permission list and navigation menu to obtain the routing table element is temporarily represented by a string and then mapped before rendering.export const handelFilterRouter = ( rights: any, menus: any, routes: any = [] ) => { for (const menu of menus) { if () { let index = ((item: any) => item === ) + 1; if (!) { if (index) { const obj = { path: , element: `sand-box${}`, }; (obj); } } else { handelFilterRouter(rights, , routes); } } } return routes; }; // Return to the final routing tableexport const handelEnd = (routes: any) => { defaulyRoutes[1].children = [...routes, ...defaulyRoutes[1].children]; return defaulyRoutes; }; // Map element into corresponding componentsexport const handelFilterElement = (routes: any) => { return ((route: any) => { = lazyLoad(); return route; }); };
import routes from "./router"; import { useRoutes } from "react-router-dom"; import { shallowEqual, useSelector } from "react-redux"; import { useState, useEffect } from "react"; import { handelFilterElement, handelEnd } from "@/utils/routersFilter"; import { deepCopy } from "@/utils/devUtils"; function App() { ("first"); const [rout, setrout] = useState(routes); const { routs } = useSelector( (state: any) => ({ routs: }), shallowEqual ); const element = useRoutes(rout); // Listen to the routing table to change and re-render useEffect(() => { // deepCopy deep copy state data cannot affect the data in the store! // handelFilterElement maps the corresponding components // handelEnd embeds the routing table into the default routing table to get the complete routing table const end = handelEnd(handelFilterElement(deepCopy(routs))); setrout(end); }, [routs]); return <div className="height-all">{element}</div>; } export default App;
The above is the detailed content of the dynamic routing instance of react-router v6. For more information about react-router v6 dynamic routing, please follow my other related articles!