React-router-dom v6 无限级嵌套路由的实现

话不多说直接上代码
以下代码为了节省空间,有简写

入口文件 index.js
root.render(<BrowserRouter><App /></BrowserRouter>
);
App组件
import React, { ReactNode, useEffect } from 'react'
import Layout from './layout'
import { Routes, Route, useNavigate } from "react-router-dom";
import NoMatch from './views/NoMatch'
import { HasAuthRoutes, IRoute } from './router';
import Login from './views/Login';
import AuthPage from './views/AuthPage';
import NoAuthPage from './views/NoAuthPage';const getRouteData = (items: IRoute[]): ReactNode => {return items.map(r => {if (r.children && r.children.length > 0) {return (<Route index={r.path == '/'} key={r.key} element={r.component}>{getRouteData(r.children)}</Route>)}return (<Route index={r.path == '/'} key={r.key} path={r.path} element={r.component}></Route>)})
}export default function App() {return (<><AuthPage><Routes><Route path="/" element={<Layout />}>{getRouteData(HasAuthRoutes)}<Route path="*" element={<NoMatch />} /></Route></Routes></AuthPage><NoAuthPage><Routes><Route path="/login" element={<Login />} /></Routes></NoAuthPage></>)
}
Layout.tsx
import React, { Suspense, useEffect, useState } from 'react';
import { LaptopOutlined, NotificationOutlined, UserOutlined } from '@ant-design/icons';
import { Breadcrumb, Layout, Menu, MenuProps } from 'antd';
import { IRoute, HasAuthRoutes, NoAuthRoutes } from '../router'
import { findkeyByPath, findPathByKey } from '../utils'
import { Outlet, Link, useLocation } from 'react-router-dom'
import { useNavigate } from 'react-router'const { Header, Content, Sider } = Layout;const getItems = (items: IRoute[]): MenuProps['items'] => items.map(r => ({// label: <Link to={r.key || ''}>{r.title}</Link>,label: r.title,key: r.key,children: r.children ? getItems(r.children) : null,
}))const findKey = () : { acKeys: string[], openKeys: string[] } => {let pathname = window.location.pathnamelet currentRoute = findkeyByPath(pathname, HasAuthRoutes) as { route: IRoute, openKeys: string[] }return {acKeys: [currentRoute.route.key],openKeys: currentRoute.openKeys}
}const MyLayout = () => {let navigate = useNavigate();const [ openKeys, setOpenKeys ] = useState<string[]>([])const { pathname } = useLocation()const onMenuSelect = (arg: { keyPath: any[], key: string }): void => {console.log(arg, 'arg');let route = findPathByKey(arg.key, HasAuthRoutes) as IRouteconsole.log('route', route);navigate(route.path as string);}const onOpenChange = (arg: any) => {console.log(arg, 'arg');setOpenKeys(arg)}return <div><Layout><Header className="header"><div className="logo" /><Menu theme="dark"onOpenChange={onOpenChange}defaultSelectedKeys={findKey().acKeys}mode="horizontal" onSelect={onMenuSelect} items={getItems(HasAuthRoutes)} /></Header><Layout><Sider width={200} className="site-layout-background"><Menumode="inline"defaultSelectedKeys={findKey().acKeys}defaultOpenKeys={findKey().openKeys}style={{height: '100%',borderRight: 0,}}// onOpenChange={onOpenChange}onSelect={onMenuSelect}items={getItems(HasAuthRoutes)}/></Sider><Layoutstyle={{padding: '0 24px 24px',}}><ContentclassName="site-layout-background"style={{padding: 24,margin: 0,minHeight: 280,}}><Suspense fallback="xx"><Outlet /></Suspense></Content></Layout></Layout></Layout></div>
}export default MyLayout

以上Suspense 是用于页面内容懒加载,<Outlet/> 类似于vuerouter-view组件

Utils.ts
import { HasAuthRoutes, IRoute } from '../router'export const findPathByKey = (key: string, routes: IRoute[]): IRoute | undefined => {// 递归查对应的pathif (routes && routes.length > 0) {for (let route of routes) {if (route.key == key && route.path) {return route} else {let item = findPathByKey(key, route.children || [])if (item) return item}}}
}export const findkeyByPath = (path: string, routes: IRoute[]): IRoute | undefined => {// 递归查对应的keyif (routes && routes.length > 0) {for (let route of routes) {console.log(route, 'route');if (route.path == path) {return route} else {if (route.children && route.children.length > 0) {let findItem = findkeyByPath(path, route.children || [])if (findItem) return findItem} }}}
}export const findOpenKeysByKey = (key: string, initRoutes: IRoute[]) => {// 找到所有的父级的key组成数组let opKeys: any[] = []const findFunc = (key: string, routes: IRoute[]) => {for (let i = 0; i < routes.length; i++) {const route = routes[i];if(route.children?.some(it1 => it1.key === key)){// 如果子元素中有key是和他一样的话,那就先把最后一层的key push进去并且放最后一项opKeys.unshift(route.key)// 重新递归findFunc(route.key, initRoutes)}else{// 继续递归findFunc(key, route.children || [])}}return opKeys}return findFunc(key, initRoutes)}
routes.ts
import { ReactNode, lazy } from 'react'const Home = lazy(() => import('../views/Home'));
const About = lazy(() => import('../views/About'));
const Dashboard = lazy(() => import('../views/Dashboard'));
const VeryManyComp = lazy(() => import('../views/VeryManyComp'));
const UserList = lazy(() => import('../views/UserList'));
const UserDetail = lazy(() => import('../views/UserDetail'));
const Login = lazy(() => import('../views/Login'));export interface IRoute {title: stringkey: stringpath?: stringexact?: booleanchildren?: IRoute[]component?: ReactNode
}export const HasAuthRoutes: IRoute[] = [{title: '首页',key: 'home',path: '/',exact: true,component: <Home />},{title: '关于',key: 'about',path: '/about',exact: true,component: <About />},{title: 'dashboard',key: 'dashboard',path: '/dashboard',exact: true,component: <Dashboard />},{title: 'test1',key: 'test1',children: [{title: 'test1-1',key: 'test1-1',children: [{title: 'test1-1-1',key: 'test1-1-1',children: [{title: 'test1-1-1-1',key: 'test1-1-1-1',path: '/test1-1-1-1',component: <VeryManyComp />}]}]}]},{title: '用户',key: 'user',path: '/user',children: [{title: '用户列表',key: 'list',children: [{title: '用户小明1',key: 'xm',path: '/user/list/xm',component: <UserList />,exact: true,}]},{title: '用户详情',key: 'detail',path: '/user/detail',component: <UserDetail />,},]}
]

使用自定义hooks来完成菜单自动高亮

layout.tsx

import React, { Suspense, useEffect, useState } from 'react';
import { LaptopOutlined, NotificationOutlined, UserOutlined } from '@ant-design/icons';
import { Breadcrumb, Layout, Menu, MenuProps } from 'antd';
import { IRoute, HasAuthRoutes, NoAuthRoutes } from '../router'
import { findkeyByPath, findPathByKey } from '../utils'
import { Outlet, Link, useLocation } from 'react-router-dom'
import { useNavigate } from 'react-router'
import { useMenu } from '../hooks'const { Header, Content, Sider } = Layout;const getItems = (items: IRoute[]): MenuProps['items'] => items.map(r => ({// label: <Link to={r.key || ''}>{r.title}</Link>,label: r.title,key: r.key,children: r.children ? getItems(r.children) : null,
}))const findKey = (): { acKeys: string[], openKeys: string[] } => {// const { pathname } = useLocation()let pathname = window.location.pathnamelet currentRoute = findkeyByPath(pathname, HasAuthRoutes) as { route: IRoute, openKeys: string[] }return {acKeys: [currentRoute.route.key],openKeys: currentRoute.openKeys}
}const MyLayout = () => {let navigate = useNavigate();const [route, openKeys] = useMenu()console.log(route, openKeys, 'route, openKeys');const onMenuSelect = (arg: { keyPath: any[], key: string }): void => {console.log(arg, 'arg');// browserHistory.push(arg.key);let route = findPathByKey(arg.key, HasAuthRoutes) as IRouteconsole.log('route', route);navigate(route.path as string);}const onOpenChange = (arg: any) => {console.log(arg, 'arg');// setOpenKeys(arg)}return <Menumode="inline"defaultSelectedKeys={[route.key]}defaultOpenKeys={openKeys}style={{height: '100%',borderRight: 0,}}onSelect={onMenuSelect}items={getItems(HasAuthRoutes)}/>
}export default MyLayout

hooks.tsx

import { useEffect, useMemo } from 'react'
import { useLocation } from 'react-router-dom'
import { HasAuthRoutes, IRoute } from '../router'
import { findkeyByPath, findOpenKeysByKey } from '../utils'export function useMenu(): any[] {const { pathname } = useLocation()const result = useMemo(() => {let route = findkeyByPath(pathname, HasAuthRoutes)let openKeys = findOpenKeysByKey(route?.key as string, HasAuthRoutes)return [ route, openKeys ]}, [pathname])return result
}

utils.tsx

export const findOpenKeysByKey = (key: string, initRoutes: IRoute[]) => {// 找到所有的父级的key组成数组let opKeys: any[] = []const findFunc = (key: string, routes: IRoute[]) => {for (let i = 0; i < routes.length; i++) {const route = routes[i];if(route.children?.some(it1 => it1.key === key)){// 如果子元素中有key是和他一样的话,那就先把最后一层的key push进去并且放最后一项opKeys.unshift(route.key)// 重新递归findFunc(route.key, initRoutes)}else{// 继续递归findFunc(key, route.children || [])}}return opKeys}return findFunc(key, initRoutes)}

React中无限级嵌套路由的实现(RRD-V6)相关推荐

  1. this指向、数据双向流、传递参数、JSX中循环、React中样式、路由、引入资源的其它方式、create-react-app脚手架、事件处理、获取数据、UI框架推荐、pc桌面应用electronjs

    改变this指向的几种方式: //1.使用箭头函数代替原始函数写法:getState=()=>{}//2.在函数调用时给函数名加bind(this)方法:(bind中第一个参数表示修改this指 ...

  2. lumen 项目根目录_在Lumen路由中使用嵌套路由群组

    前段时间写的古诗词文api使用了,Dingo/api,tymondesigns/jwt-auth. 为了更加方便,而不是局限于Dingo/api框架中,我使用spatie/laravel-fracta ...

  3. 使用React Router v4的嵌套路由

    React Router v4 introduced a new declarative, component based approach to routing. With that approac ...

  4. 在React中使用React Router v6

    具有多个视图的单页应用程序 (SPA) 需要有一种路由机制,以便在这些不同视图之间导航,而无需刷新整个网页.这可以通过使用诸如React Router之类的路由库来处理. 在本教程中,让我们看看如何使 ...

  5. js路由在php上面使用,React中路由使用详解

    这次给大家带来React中路由使用详解,React中路由使用的注意事项有哪些,下面就是实战案例,一起来看一下. 路由 通过 URL 映射到对应的功能实现,React 的路由使用要先引入 react-r ...

  6. react 动态路 嵌套动子路由_2020年,我是如何从一名Vueer转岗到React阵营

    前言 大家好呀,我是一名专注于Vue的前端萌新. 国庆后如约和大家见面了.经历了金九银十和十一长假后的我也要开始努力写文章了,这次主要是分享下我是如何快速横切到React技术栈.如果看文章的你正在学习 ...

  7. react路由嵌套路由及路由传参

    因为react的嵌套路由跟vue比就像屎一样 不好写 所以在使用的时候建议使用react-router-config来配置路由 会相对轻松 第一步 先安装路由依赖 yarn add react-rou ...

  8. react ——withRouter——页面隐式传值—嵌套路由——渲染方式——自定义导航组件

    withRouter import {Route,Switch,withRouter} from "react-router-dom" withRouter高阶组件增强组件--获取 ...

  9. 搭建SPA项目SPA项目中使用路由嵌套路由

    目录 一.vue-cli建立SPA项目 1.1 安装vue-cli 1.2 命令构建SPA项目 1.3 导入编码器 1.4 SPA项目运行访问过程 二.SPA项目使用路由 2.1 定义组件 2.2 定 ...

最新文章

  1. 图像拼接--Seam-Driven Image Stitching
  2. @程序员:Java平均工资再次上涨,光张年限不涨薪的我慌了!
  3. 神经网络的分类行为怎么就不能是一种力的行为?
  4. 前百度智能硬件产品负责人邓晗:语⾳交互设计的原则
  5. some VM operation when debugging appointment startup
  6. OpenGL 库 简介
  7. mingW与cygwin
  8. 遍历删除List中的元素,会报错? 用iterator.remove() 完美解决
  9. mybatisplus 结果_MyBatis Plus 将查询结果封装到指定实体
  10. python常用方法_python常用方法(持续更新)
  11. jQuery学习之路(1)-选择器
  12. 三人表决器Verilog
  13. 计算机临时保存信息,Windows临时文件夹是什么,Windows临时文件夹保存位置在哪里?...
  14. 系统文件IO与标准文件IO
  15. pg_auto_failover 之三 automated failover
  16. 【安全知识分享】PPTX|食堂食品安全卫生知识培训(65页)(附下载)
  17. 从前慢-Shiro和JWT
  18. 华硕主板如何设置开机自启_华硕主板每次开机都进bios 华硕主板开机总是自动进入了BIOS设置界面怎么办?...
  19. 对 算术基本定理 的研究
  20. OpenAi 语法修正

热门文章

  1. 腾讯同事内推的那位Linux C/C++后端开发同学面试没过......
  2. 3.28“地球停电一小时”——世界节电日
  3. Nuke 常用文档收集
  4. 考研英语二真题文章重点单词
  5. opencv 插值方式
  6. .考试倒计时41天!来提分啦!
  7. 【Java】生产者消费者模式的实现
  8. Vue3CLI(脚手架)
  9. HTML 排版与标签(五)
  10. VS2019报错写入/读入访问权限冲突