菜单栏展开回收、页面刷新时确保菜单状态与当前页面匹配
- 1.菜单栏展开和回收事件
- [2. 刷新时默认当前选中项样式的处理](#2. 刷新时默认当前选中项样式的处理)
-
- [2.1 刷新页面菜单保持用户之前的选中状态](#2.1 刷新页面菜单保持用户之前的选中状态)
- [2.2 配置展开项openKeys的初始值](#2.2 配置展开项openKeys的初始值)
javascript
export const asyncRouterMap = [
{
path: '/page1',
title: 'page1',
icon: 'HomeOutlined',
}, {
path: '/page2',
title: 'page2',
icon: 'HomeOutlined',
}, {
path: '/page3',
title: 'page3',
icon: 'HomeOutlined',
children: [
{
path: '/page301',
title: 'page301',
icon: 'HomeOutlined',
}, {
path: '/page302',
title: 'page302',
icon: 'HomeOutlined',
}, {
path: '/page303',
title: 'page303',
icon: 'HomeOutlined',
}
]
},{
path: '/page4',
title: 'page4',
icon: 'HomeOutlined',
children: [
{
path: '/page401',
title: 'page401',
icon: 'HomeOutlined',
}, {
path: '/page402',
title: 'page402',
icon: 'HomeOutlined',
}
]
}
]
使用path当作Menu的key.
1.菜单栏展开和回收事件
点击菜单,收起其他展开的所有菜单,只留下一个。
Menu的onOpenChange
方法:SubMenu 展开/ 关闭的回调函数,展开和回收某项菜单时的事件。
Menu的openKeys
属性:当前展开的SubMenu菜单项的key数组,当前菜单展开项的key数组。
当前要展开哪一项的openKeys将来是要变化的,需要存到state中。
javascript
state={
openKey:['']
}
//展开和回收某项菜单,把openKeys的数组值变为keys数组的最后一项,只要一项是展开的,就是我刚点击的这一项
handleOpenChange=(keys)=>{
//keys是一个字符串数组,记录了当前哪一项是展开的(用key来记录)
//console.log(keys)
/* 点击page3 keys输出["",'/page3'],再点击page4 keys输出["",'/page4'],点击谁keys数组的最后一项就是谁*/
this.setState({
openKeys:[keys[keys.length-1]]
})
}
<Menu
defaultSelectedKeys={['/home']}
mode="inline"
theme="dark"
style={{ height: "100vh" }}
openKeys={openKeys}
onOpenChange={this.handleOpenChange}
>
2. 刷新时默认当前选中项样式的处理
根据用户的操作习惯,选中一个二级菜单节点的时候,刷新页面的时候应该保持用户之前的选中状态,并且二级菜单展开项应该默认展开。
2.1 刷新页面菜单保持用户之前的选中状态
现在的情况是点击page2页面,组件在page2页面,在这时刷新页面,导航栏的高亮选中变成了第一项,但是组件还是page2页面,出现了选中菜单与页面不符合的情况,我们希望的是点击了刷新页面之后组件在page2页面并且导航栏选中项也是这个。
Menu的selectedKeys
属性:表示当前样式所在的选中项key,不能直接写死,通过获取当前的路径拿到key。
怎么获取当前路径呢?对于类组件,使用this.props.location.pathname
拿到的就是当前页面的路径;对于函数式组件,使用hooks的useLocation().pathname
拿到当前页面的路径。
获取到当前页面的路径后作为值修改selectedKeys的值。
javascript
state={
openKey:[''],
selectedKey:['']
}
/* this.changeSelectKeys()这个函数要在要在componentDidMount中调用一次,
再在componentUpdate中再调用。
这是因为componentDidMount只在第一次render之后执行一次,
后续的rendr执行后会执行componentDidUpdate;
从登录页进入到首页后会先mount,然后有setState,会发生update,
点击切换菜单栏只会再执行componentUpdate,
不会再执行componentDidMount了。*/
/*在componentDidUpdate生命周期方法中加入
判断当前页面路径是否与上一个页面路径相同
是为了避免不必要的状态更新和重新渲染,从而防止死循环的发生。
如果没有这个判断,每次组件更新时都会执行changeSelectedKey方法,
该方法会根据当前路径更新菜单状态。
然而,更新状态又会导致组件重新渲染,这将再次触发componentDidUpdate方法,形成一个无限循环。
通过添加判断,我们可以确保只有在当前页面路径与上一个页面路径不同时才会更新菜单状态,
从而避免了不必要的循环更新。*/
componentDidUpdate(prevProps) {
// 这里必须要有这个前后路径是否一致的判断,否则会出现死循环
if (prevProps.location.pathname !== this.props.location.pathname) {
this.changeSelected();
}
}
componentDidMount() {
this.changeSelected();
}
changeSelected = () => {
//根据当前页面路由修改当前选中的菜单项key数组
//获取当前页面路径(切割后的key) 由于我们设计的当前页面路径与key不完全一致需要做切割
const currentRoute = this.props.location.pathname.split('/index')[1]
this.setState({
selectedKey: [currentRoute]
})
}
<Menu
defaultSelectedKeys={['/home']}
selectedKeys={selectedKey}
mode="inline"
theme="dark"
style={{ height: "100vh" }}
openKeys={openKeys}
onOpenChange={this.handleOpenChange}
>
现在能够实现的是页面刷新保持用户的选中状态,但是当我们点击的是page301再刷新页面时,虽然该菜单项会被选中,但是菜单栏中page301没有被展开,需要再进行下面的配置。
2.2 配置展开项openKeys的初始值
需要修改openKeys数组,openKeys的展开项是不能写死的,刷新页面后谁展开是跟路径有关的。
整体思路是:拿着this.props.location.pathname与items数组的每一项的children的key值进行对比,如果找到了相等了,就要他上一级的key给到openKeys数组的元素,作为初始值。
(这个思路更加通用一些)
整体思路:根据this.props.location.pathname拿到它上一级的key,比如'/page3/page301'要想办法拿到'/page3','/page3/page301/page30101'要想办法拿到'/page3/page301',把openKeys的值改为拿到的它上一级的key,这个操作可以跟随changeSelectedKey操作放在一个函数中。(这种方法比较简单,但是要求key的格式必须是 /page3/page301 这样的)
javascript
changeSelected = () => {
//根据当前页面路由修改当前选中的菜单项key数组
//获取当前页面路径(切割后的key) 由于我们设计的当前页面路径与key不完全一致需要做切割
const currentRoute = this.getCurrentRoute()
// pathname: /home /student/exam /student/exam/a
// 想要的结果 /home /student /student/exam
//先拆开在拼起来
//拆开 ['',home] ['','student','exam'] ['','student','exam','a']
//取1~倒数第二项 ['student'] ['student','exam']
//拼接起来
let openKeys=[]
if(currentRoute.split('/').length>2){
openKeys=[`/${currentRoute.split('/').slice(1, -1).join('/')}`]
}
// console.log(openKeys)
this.setState({
selectedKey: [currentRoute],
openKeys
})
}
componentDidMount用于刷新页面时页面卸载又重新挂载,componentDidUpdate用于不刷新页面只点击菜单栏。
在使用React类组件时,调用updateMenuState
方法既在componentDidMount
中也在componentDidUpdate
中是为了确保无论组件是在初次挂载还是在更新后,都能根据当前的路由路径正确设置菜单的选中状态和展开状态。
这两个生命周期方法各自承担着不同的职责:
-
componentDidMount
:这个方法在组件首次渲染到DOM中后被调用。此时,你可以进行API调用、设置状态等操作。对于导航菜单来说,当组件首次加载时,你需要根据当前的路由路径初始化菜单的状态,包括哪个菜单项被选中以及哪些子菜单应该展开。因为在应用首次加载时,用户可能直接通过URL访问特定页面,所以需要在这个时刻根据URL设置正确的菜单状态。 -
componentDidUpdate
:这个方法在组件更新后被调用。在React中,组件的更新可能由props或state的改变引起,也可能是由于父组件的重新渲染触发。在导航菜单的情况下,用户可能通过点击链接或其他方式导致路由的变化,因此需要在组件更新后再次检查当前的路由路径,以确保菜单状态与当前页面匹配。因此,在componentDidUpdate
中调用updateMenuState
方法,可以在组件更新后根据新的路由路径更新菜单状态,保持菜单的正确显示。
参考内容:React 中使用antd,刷新时被选中的menu二级菜单初始化的展开问题
React+Ant Design 4.4.1实现左侧二级导航(可配置路由、所有路由层级可统一、可根据路由高亮菜单项、刷新时可自动展开定位到当前路由)