🌈个人主页:前端青山
🔥系列专栏:React篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来React篇专栏内容:react后台管理系统(二)
前言
本文档旨在详细说明如何在一个基于React的应用程序中实现左侧菜单栏的点击跳转功能,并确保在页面刷新后菜单栏能够保持选中状态。通过本文档,读者将了解到如何配置路由、创建页面组件、处理编程式跳转、设置重定向和404页面,以及如何利用useLocation
Hook来保持菜单栏的状态。本文档适合对React和React Router有一定了解的前端开发人员。
目录
前言
8.点击左侧菜单跳转Url地址
9.创建页面对应的组件
9.配置路由
10.渲染路由组件
11.路由的重定向以及404页面
12.刷新页面左侧菜单栏保持选中状态
总结
8.点击左侧菜单跳转Url地址
注意观察Menu组件的属性表,找到onClick属性
编程式跳转
// 方式1
import {withRouter} from 'react-router-dom'
class App extends Component {render () {return (<button onClick = { () => {this.props.history.push()}}>跳转</button>)}
}
export default withRouter(App)
// 方式2
import {withRouter} from 'react-router-dom'
function App (props) {return (<button onClick = { () => {props.history.push()}}>跳转</button>)
}
export default withRouter(App)
// 方式3 ---- useHistory https://reactrouter.com/web/api/Hooks/usehistory
import { useHistory } from 'react-router-dom'
function App () {const history = useHistory() // 不可以在组件的内部事件中调用return (<button onClick = { () => {history.push()}}>跳转</button>)
}
export default App
import React from 'react'
import { Menu } from 'antd';
import menus from '../../router/menus'
import { useHistory } from 'react-router-dom'
const { SubMenu } = Menu;
// submenu keys of first level
const keyArr = []
menus.forEach(item => { // 只需要一级菜单项即可keyArr.push(item.path)
})
// const rootSubmenuKeys = ['sub1', 'sub2', 'sub4'];
const rootSubmenuKeys = keyArr
const SideMenu = () => {// 用来 编程式跳转const history = useHistory()const [openKeys, setOpenKeys] = React.useState([]);
const onOpenChange = keys => {const latestOpenKey = keys.find(key => openKeys.indexOf(key) === -1);if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {setOpenKeys(keys);} else {setOpenKeys(latestOpenKey ? [latestOpenKey] : []);}};const renderMenus = (menus) => {return menus.map(item => {// 判断当前的选项有没有子菜单if (item.children) { // 有子级菜单return (<SubMenu key={ item.path } icon={ item.icon } title={item.title}>{/* 递归调用自身,渲染多级菜单 */}{ renderMenus(item.children) }</SubMenu>)} else {// path具有唯一性,key也具有唯一性// 为了后续操作方便,此时建议将path作为遍历的key值return <Menu.Item key={ item.path } icon = { item.icon }>{ item.title }</Menu.Item>}})}const changeUrl = ({ key }) => {// console.log(key) // 为什么在渲染菜单栏时 把 item.path 置为 key属性// console.log(item)// 编程式跳转 props.history.pushhistory.push(key)}return (<Menu onClick = { changeUrl } theme="dark" mode="inline" openKeys={openKeys} onOpenChange={onOpenChange} >{renderMenus(menus)}{/* <Menu.Item key="1">Option 1</Menu.Item><SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One"><Menu.Item key="1">Option 1</Menu.Item><Menu.Item key="2">Option 2</Menu.Item><Menu.Item key="3">Option 3</Menu.Item><Menu.Item key="4">Option 4</Menu.Item></SubMenu> */}</Menu>);
};
export default SideMenu
9.创建页面对应的组件
点击左侧菜单跳转至页面
9.配置路由
路由文件menus 中 配置组件
import React from 'react'
import { HomeOutlined,PictureOutlined,NotificationOutlined,UsergroupDeleteOutlined,SwitcherOutlined
} from '@ant-design/icons'
import Home from './../views/home/Index'
const menus = [{title: '系统首页',path: '/home',key: '0-0',icon: <HomeOutlined />,component: Home},{title: '轮播图管理',path: '/banner',key: '0-1',icon: <PictureOutlined />,children: [{title: '轮播图列表',path: '/banner/list',key: '0-1-0',component: React.lazy(() => import('./../views/banner/Index'))},{title: '添加轮播图',path: '/banner/add',key: '0-1-1',component: React.lazy(() => import('./../views/banner/Add'))},]},{title: '首页数据管理',path: '/homeData',key: '0-2',icon: <NotificationOutlined />,children: [{title: '秒杀列表',path: '/homeData/skill',key: '0-2-0',component: React.lazy(() => import('./../views/homeData/Skill'))
},{title: '推荐列表',path: '/homeData/recommend',key: '0-2-1',component: React.lazy(() => import('./../views/homeData/Recommend'))}]},{title: '用户管理',path: '/users',key: '0-3',icon: <UsergroupDeleteOutlined />,children: [{title: '用户列表',path: '/users/list',key: '0-3-0',component: React.lazy(() => import('./../views/users/List'))},{title: '管理员列表',path: '/users/adminList',key: '0-3-1',component: React.lazy(() => import('./../views/users/AdminList'))}]},{title: '商品管理',path: '/pro',key: '0-4',icon: <SwitcherOutlined />,children: [{title: '商品列表',path: '/pro/list',key: '0-4-0',component: React.lazy(() => import('./../views/pro/List'))},{title: '筛选商品',path: '/pro/search',key: '0-4-1',component: React.lazy(() => import('./../views/pro/Search'))}]}
]
export default menus
10.渲染路由组件
创建路由组件 layout/main/RouterView
Layout/main/index.jsx
import RouterView from './RouterView';
<ContentclassName="site-layout-background"style={{margin: '24px 16px',padding: 24,minHeight: 280,}}>{/* Content */}<RouterView></RouterView></Content>
export default App
渲染路由
import React, { Suspense } from 'react'
import { Switch, Route } from 'react-router-dom'
import { Spin } from 'antd';
import menus from './../../router/menus'
function RouterView() {const renderRoute = (menus) => {return menus.map(item => {if (item.children) {// 递归调用return renderRoute(item.children)} else {return <Route path = { item.path } key = { item.path } component = { item.component } />}})}return (<Suspense fallback = { <Spin /> }><Switch>{/* <Route path="" component={} /> */}{renderRoute(menus)}</Switch></Suspense>)
}
export default RouterView
递归调用的函数,如果写成jsx代码形式,在jsx代码中 调用回调函数会发生什么情况
以下代码仅供参考--不要在项目中使用
import React, { Suspense, Fragment } from 'react'
import { Switch, Route } from 'react-router-dom'
import { Spin } from 'antd';
import menus from './../../router/menus'
import Home from './../../views/home/Index'
import UserList from './../../views/users/List'
function RouterView() {const renderRoute = (menus) => {return menus.map(item => {if (item.children) {// 递归调用return renderRoute(item.children) // return <Fragment key={item.path}>{ renderRoute(item.children) }</Fragment>} else {return <Route path = { item.path } key = { item.path } component = { item.component } />}})}return (<Suspense fallback = { <Spin /> }>{/* Switch 内 如果写 非 Route 或者是 非 Redirect元素,哪怕其中包含了Route也只有第一个元素会生效*/}<Switch>{/* <Route path="" component={} /> */}{renderRoute(menus)}<div ><Route path="/home111" component = { Home } /></div><p><Route path="/user111" component = { UserList } /> </p></Switch></Suspense>)
}
export default RouterView
11.路由的重定向以及404页面
修改menus.js,添加redirect选项
import React from 'react'
import { HomeOutlined,PictureOutlined,NotificationOutlined,UsergroupDeleteOutlined,SwitcherOutlined
} from '@ant-design/icons'
import Home from './../views/home/Index'
const menus = [{title: '系统首页',path: '/home',key: '0-0',icon: <HomeOutlined />,component: Home},{title: '轮播图管理',path: '/banner',key: '0-1',icon: <PictureOutlined />,redirect: '/banner/list', // ***************************children: [{title: '轮播图列表',path: '/banner/list',key: '0-1-0',component: React.lazy(() => import('./../views/banner/Index'))},{title: '添加轮播图',path: '/banner/add',key: '0-1-1',component: React.lazy(() => import('./../views/banner/Add'))},]},{title: '首页数据管理',path: '/homeData',key: '0-2',icon: <NotificationOutlined />,redirect: '/homeData/skill',// ***************************children: [{title: '秒杀列表',path: '/homeData/skill',key: '0-2-0',component: React.lazy(() => import('./../views/homeData/Skill'))
},{title: '推荐列表',path: '/homeData/recommend',key: '0-2-1',component: React.lazy(() => import('./../views/homeData/Recommend'))}]},{title: '用户管理',path: '/users',key: '0-3',icon: <UsergroupDeleteOutlined />,redirect: '/users/list',// ***************************children: [{title: '用户列表',path: '/users/list',key: '0-3-0',component: React.lazy(() => import('./../views/users/List'))},{title: '管理员列表',path: '/users/adminList',key: '0-3-1',component: React.lazy(() => import('./../views/users/AdminList'))}]},{title: '商品管理',path: '/pro',key: '0-4',icon: <SwitcherOutlined />,redirect:"/pro/list",// ***************************children: [{title: '商品列表',path: '/pro/list',key: '0-4-0',component: React.lazy(() => import('./../views/pro/List'))},{title: '筛选商品',path: '/pro/search',key: '0-4-1',component: React.lazy(() => import('./../views/pro/Search'))}]}
]
export default menus
创建404页面 views/error/NotFound/Inex.jsx
import React from 'react'
function NotFound() {return (<div>404</div>)
}
export default NotFound
import React, { Suspense } from 'react'
import { Switch, Route, Redirect } from 'react-router-dom'
import { Spin } from 'antd';
import menus from './../../router/menus'
import NotFound from './../../views/error/NotFound/Index'
function RouterView() {const renderRoute = (menus) => {return menus.map(item => {if (item.children) {// 递归调用return renderRoute(item.children)} else {return <Route path = { item.path } key = { item.path } component = { item.component } />}})}const renderRedirect = (menus) => {return menus.map(item => {if (item.redirect) {return <Redirect exact key={ item.key } from = { item.path } to = { item.redirect } />} else {return null}})}return (<Suspense fallback = { <Spin /> }><Switch>{/* <Route path="" component={} /> */}{renderRoute(menus)}{// 渲染重定向以及404页面renderRedirect(menus)}<Redirect exact from = "/" to="/home" /><Route component = { NotFound } /></Switch></Suspense>)
}
export default RouterView
12.刷新页面左侧菜单栏保持选中状态
使用 useLocation 获取到地址栏中的地址,解构得到pathname属性,设置展开菜单项 openKeys为 字符串类型的数组
设置 selectedKeys 为pathname。-------- 注意数据类型
import React from 'react'
import { Menu } from 'antd';
import menus from '../../router/menus'
import { useHistory, useLocation } from 'react-router-dom' // =+++++++++++
const { SubMenu } = Menu;
// submenu keys of first level
const keyArr = []
menus.forEach(item => { // 只需要一级菜单项即可keyArr.push(item.path)
})
// const rootSubmenuKeys = ['sub1', 'sub2', 'sub4'];
const rootSubmenuKeys = keyArr
const SideMenu = () => {// 获取地址栏中的地址const location = useLocation() // =+++++++++++const { pathname } = location // /banner/list // =+++++++++++console.log(location)// 用来 编程式跳转const history = useHistory()// 设置默认打开哪一项 ---- 参数是 string[] ['/banner']// // =+++++++++++const [openKeys, setOpenKeys] = React.useState(['/' + pathname.split('/')[1] ]);
const onOpenChange = keys => {const latestOpenKey = keys.find(key => openKeys.indexOf(key) === -1);if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {setOpenKeys(keys);} else {setOpenKeys(latestOpenKey ? [latestOpenKey] : []);}};const renderMenus = (menus) => {return menus.map(item => {// 判断当前的选项有没有子菜单if (item.children) { // 有子级菜单return (<SubMenu key={ item.path } icon={ item.icon } title={item.title}>{/* 递归调用自身,渲染多级菜单 */}{ renderMenus(item.children) }</SubMenu>)} else {// path具有唯一性,key也具有唯一性// 为了后续操作方便,此时建议将path作为遍历的key值return <Menu.Item key={ item.path } icon = { item.icon }>{ item.title }</Menu.Item>}})}const changeUrl = ({ key }) => {// console.log(key) // 为什么在渲染菜单栏时 把 item.path 置为 key属性// console.log(item)// 编程式跳转 props.history.pushhistory.push(key)}return (<Menu onClick = { changeUrl } theme="dark" mode="inline" openKeys={openKeys} selectedKeys = { [pathname] } // =+++++++++++onOpenChange={onOpenChange} >{renderMenus(menus)}{/* <Menu.Item key="1">Option 1</Menu.Item><SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One"><Menu.Item key="1">Option 1</Menu.Item><Menu.Item key="2">Option 2</Menu.Item><Menu.Item key="3">Option 3</Menu.Item><Menu.Item key="4">Option 4</Menu.Item></SubMenu> */}</Menu>);
};
export default SideMenu
总结
本文档通过详细的步骤和示例代码,展示了如何在React应用中实现左侧菜单栏的点击跳转功能,并确保在页面刷新后菜单栏能够保持选中状态。主要涵盖了以下几个方面:
- 编程式跳转:介绍了如何使用
useHistory
Hook进行编程式跳转。 - 创建页面组件:展示了如何创建和配置各个页面组件。
- 配置路由:详细说明了如何在路由文件中配置组件和路径。
- 渲染路由组件:介绍了如何在布局组件中渲染路由组件。
- 路由的重定向及404页面:讲解了如何设置路由重定向和404页面。
- 保持菜单栏选中状态:使用
useLocation
Hook获取当前路径,并设置菜单栏的选中状态。
通过本文档的学习,能够熟练掌握在React应用中实现左侧菜单栏点击跳转及状态保持的方法,从而提升用户体验和开发效率。