案例背景
react-router-dom 更新到 v6 着实是一次大改,很多之前重要的组件和个性都改掉了,最显著的就是替换了 Switch,Route 里的 Component 属性等等。当然,对此次发现的问题的首恶是,更新删除了路由组件 (class) 默认自带的三个属性 –match, history, location。不仅让编程式路由导航的罕用写法生效了,antd 动静生成 menu 的 view 也受到了影响。同时 react-router-dom 在鼎力推广函数式组件的应用,所以能够通过一些 hook 来获取 match,history 和 location,但类式组件就没那么侥幸啦,本文次要针对 router 的更新对类式组件的影响探讨。
原因是之前 Menu 中的 selectedKeys 属性须要通过 this.props.location.pathname,从以后路由组件获取当初的 path,router 更新后当然就只能另求他法了。
这里先要阐明一下为什么不将 Menu 中的 defaultSelectedKeys 设置为 ’/home’:如果用户是从 ’/’ 被重定向到 ’/home’,此时会呈现问题,因为页面会渲染两次导致第二次无奈应用 default。
老版本的动静生成 menu 的办法如下:
export default class LeftNav extends Component {
state = {collapsed: false,};
onCollapse = collapsed => {this.setState({ collapsed});
};
createMenuNodes = (menuList) => {
return menuList.map(item => {
return item.children ?
(<SubMenu key={item.key} icon={item.icon} title={item.title}>
{this.createMenuNodes(item.children)}
</SubMenu>
) :
(<Menu.Item key={item.key} icon={item.icon}>
<Link to={item.key}>{item.title}</Link>
</Menu.Item>
)
})
}
render() {const { collapsed} = this.state
const path = this.props.location.pathname
return (<Sider collapsible collapsed={collapsed} onCollapse={this.onCollapse}>
<div className="logo" style={collapsed === true ? { opacity: 0} : {opacity: 1, transition: 'all 0.7s'}}> 商品后盾管理系统 </div>
<Menu theme="dark" selectedKeys={[path]} mode="inline">
{this.createMenuNodes(menuList)
}
</Menu>
</Sider>
)
}
}
解决方案
最后想到的办法应用浏览器自带的 window.location.pathname 获取,然而点击导航时却并没有触发高亮变动,起因应该是 react 的 diffing 算法并不能监测到 URL 的变动,所以没有触发 render。目前我只想到一个不太聪慧的办法,通过更新 state 获取 path,但会多出很多条代码来解决门路异样时,state 更新到 home 的问题,所以还要先判断以后 path 是不是无效的 path。很轻便,集体愚见,如果有大佬能想到更好的优化办法请多指教。(最好的优化就是不必 class 组件~)
export default class LeftNav extends Component {
state = {
collapsed: false,
path: ''
};
onCollapse = collapsed => {this.setState({ collapsed});
};
componentDidMount(){
const curPath = window.location.pathname
const effectivePaths = this.findEffectivePaths(menuList).flat() // 扁平化数组
if (effectivePaths.indexOf(curPath) === -1) {this.setState({path: '/home'}) // 如果不是无效 path,就指向 home
} else {this.setState({path: curPath})
}
}
// 统计无效的 path
findEffectivePaths = (menuList) => {
return menuList.map(item => {if (item.children) {return this.findEffectivePaths(item.children)
} else {return item.key}
})
}
// 动静生成 Menu,map+ 递归(也能够通过 reduce+ 递归)createMenuNodes = (menuList) => {
return menuList.map(item => {
return item.children ?
(<SubMenu key={item.key} icon={item.icon} title={item.title}>
{this.createMenuNodes(item.children)}
</SubMenu>
) :
(<Menu.Item key={item.key} icon={item.icon}>
<Link to={item.key} onClick={() => {this.setState({path: item.key})}}>{item.title}</Link>
</Menu.Item>
)
})
}
render() {const { collapsed, path} = this.state
return (<Sider collapsible collapsed={collapsed} onCollapse={this.onCollapse}>
<div className="logo" style={collapsed === true ? { opacity: 0} : {opacity: 1, transition: 'all 0.7s'}}> 商品后盾管理系统 </div>
<Menu theme="dark" selectedKeys={[path]} mode="inline">
{this.createMenuNodes(menuList)
}
</Menu>
</Sider>
)
}
}