乐趣区

关于react.js:reactrouterdom-v6更新对antd动态生成menu菜单的影响

案例背景

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>
        )
    }
}
退出移动版