react-router+antD/ 如何在按下 back/forward 按钮时高亮菜单项?

react-router+antD/ How to highlight a menu item when press back/forward button?

我创建了一个菜单并想突出显示我选择的项目,我做到了。但是当我按下 back/forward 按钮时,菜单项没有突出显示。我应该怎么办?

我尝试使用 addEventListener 但失败了。

有人可以给点建议吗?

class Sidebar extends React.Component {
    constructor(props) {
        super(props);
        this.state={
            test: "home"
        }
        this.menuClickHandle = this.menuClickHandle.bind(this);
    }

    componentWillMount(){
        hashHistory.listen((event)=>{
            test1 = event.pathname.split("/");
        });
        this.setState({
            test:test1[1]
        });
    }

    menuClickHandle(item) {
        this.props.clickItem(item.key);
    }

    onCollapseChange() {
        this.props.toggle();
    }

    render() {
        var {collapse} = this.props;
        return (
            <aside className="ant-layout-sider">
                <Menu mode="inline" theme="dark" defaultSelectedKeys={[this.state.test || "home"]} onClick={this.menuClickHandle.bind(this)}>
                    <Menu.Item key="home">
                        <Link to="/home">
                            <Icon type="user"/><span className="nav-text">用户管理</span>
                        </Link>
                    </Menu.Item>
                    <Menu.Item key="banner">
                        <Link to="/banner">
                            <Icon type="setting"/><span className="nav-text">Banner管理</span>
                        </Link>
                    </Menu.Item>
                </Menu>
                <div className="ant-aside-action" onClick={this.onCollapseChange.bind(this)}>
                    {collapse ? <Icon type="right"/> : <Icon type="left"/>}
                </div>
            </aside>
        )
    }
}

截取当前URL然后设置selectedKeys(注意不是defaultSelectedKeys)。

componentWillMount(){
        hashHistory.listen((event)=>{
            pathname = event.pathname.split("/");
            if(pathname != null){
                this.setState({
                    test:pathname[1]
                });
            }
        });
    }

我做了这样的事情,但它似乎没有反应。就像我通过按钮(不是从菜单项)导航到新页面一样,它不会更新活动 link 直到页面刷新。

import React from 'react';
import { StyleSheet, css } from 'aphrodite'
import { browserHistory, Link } from 'react-router';
import 'antd/lib/menu/style/css';
import 'antd/lib/icon/style/css';
import 'antd/lib/row/style/css';
import 'antd/lib/col/style/css';
import 'antd/lib/message/style/css';
import { appConfig } from '../../modules/config';
import { Menu, Icon, Row, Col, message } from 'antd';

const SubMenu = Menu.SubMenu;
const MenuItemGroup = Menu.ItemGroup;


const { appName } = appConfig;




const AppNavigation = React.createClass({
  getInitialState() {
        return {
          current: this.props.pathname
        };

  },
  handleClick(e) {
    browserHistory.push(e.key);
    this.setState({ current: e.key });
    return;  
  },
  render() {
    return (
    <Row className='landing-menu' type="flex" justify="space-around" align="middle"  style={{height: 55, zIndex: 1000, paddingLeft: 95, color: '#fff', backgroundColor: '#da5347', borderBottom: '1px solid #e9e9e9'}}>
        <Col span='19'>
            <Link to='/'>
          <h2 style={{fontSize: 21, color: '#fff'}}>
            {appName}
            <Icon type="rocket" color="#fff" style={{fontWeight: 200, fontSize: 26, marginLeft: 5 }}/>
          </h2>
        </Link>
        </Col>
        <Col span='5'>
            <Menu onClick={this.handleClick} selectedKeys={[this.state.current]} mode="horizontal" style={{height: 54, backgroundColor: '#da5347', borderBottom: '0px solid transparent'}}>
            <Menu.Item style={{height: 54, }} key="/">Home</Menu.Item>
            <Menu.Item style={{height: 54, }} key="/signup">Signup</Menu.Item>
            <Menu.Item style={{height: 54, }} key="/login">Login</Menu.Item>
          </Menu>
        </Col>

      </Row>
    );
  },
});


export const App = React.createClass({

  propTypes: {
    children: React.PropTypes.element.isRequired,
  },
  componentWillMount(){
    if (Meteor.userId()) {
      browserHistory.push('/student/home')
    }
  },
  render() {

    return (
        <div style={{position: 'relative'}}>
          <AppNavigation pathname={this.props.location.pathname}  />
            <div style={{minHeight: '100vh'}}>
             { this.props.children }
            </div>
        </div>
    );
  }





});

编辑:

下面的效果很好。从 react-router 传递路径名并将其作为道具弹出到 selectedKeys

import React from 'react';
import { StyleSheet, css } from 'aphrodite'
import { browserHistory, Link } from 'react-router';
import 'antd/lib/menu/style/css';
import 'antd/lib/icon/style/css';
import 'antd/lib/row/style/css';
import 'antd/lib/col/style/css';
import 'antd/lib/message/style/css';
import { appConfig } from '../../modules/config';
import { Menu, Icon, Row, Col, message } from 'antd';

const SubMenu = Menu.SubMenu;
const MenuItemGroup = Menu.ItemGroup;


const { appName } = appConfig;




const AppNavigation = React.createClass({
  getInitialState() {
        return {
          current: this.props.pathname
        };

  },
  handleClick(e) {
    browserHistory.push(e.key);
    this.setState({ current: e.key });
    return;  
  },
  render() {
    return (
    <Row className='landing-menu' type="flex" justify="space-around" align="middle"  style={{height: 55, zIndex: 1000, paddingLeft: 95, color: '#fff', backgroundColor: '#da5347', borderBottom: '1px solid #e9e9e9'}}>
        <Col span='19'>
            <Link to='/'>
          <h2 style={{fontSize: 21, color: '#fff'}}>
            {appName}
            <Icon type="rocket" color="#fff" style={{fontWeight: 200, fontSize: 26, marginLeft: 5 }}/>
          </h2>
        </Link>
        </Col>
        <Col span='5'>
            <Menu onClick={this.handleClick} selectedKeys={[this.props.pathname]} mode="horizontal" style={{height: 54, backgroundColor: '#da5347', borderBottom: '0px solid transparent'}}>
            <Menu.Item style={{height: 54, }} key="/">Home</Menu.Item>
            <Menu.Item style={{height: 54, }} key="/signup">Signup</Menu.Item>
            <Menu.Item style={{height: 54, }} key="/login">Login</Menu.Item>
          </Menu>
        </Col>

      </Row>
    );
  },
});


export const App = React.createClass({

  propTypes: {
    children: React.PropTypes.element.isRequired,
  },
  componentWillMount(){
    if (Meteor.userId()) {
      browserHistory.push('/student/home')
    }
  },
  render() {

    return (
        <div style={{position: 'relative'}}>
          <AppNavigation pathname={this.props.location.pathname}  />
            <div style={{minHeight: '100vh'}}>
             { this.props.children }
            </div>
        </div>
    );
  }


});

我可以想出一个使用 WithRouter 的解决方案

import React,{ Component } from 'react';
import { NavLink, withRouter } from 'react-router-dom';
import { Layout, Menu, Icon } from 'antd';
import PropTypes from 'prop-types';

const { Sider } = Layout;

class SideMenu extends Component{

  static propTypes = {
    location: PropTypes.object.isRequired
  }

  render() {
    const { location } = this.props;
    return (
        <Sider
          trigger={null}
          collapsible
          collapsed={this.props.collapsed}>

          <div className="logo" />
          <Menu
            theme="dark"
            mode="inline"
            defaultSelectedKeys={['/']}
            selectedKeys={[location.pathname]}>
            <Menu.Item key="/">
              <NavLink to="/">
                <Icon type="home" />
                <span>Home</span>
              </NavLink>
            </Menu.Item>
            <Menu.Item key="/other">
              <NavLink to="/other">
                <Icon type="mobile"/>
                <span>Applications</span>
              </NavLink>
            </Menu.Item>
            <Menu.Item key="/notifications">
              <NavLink to="/notifications">
                <Icon type="notification" />
                <span>Notifications</span>
              </NavLink>
            </Menu.Item>
          </Menu>
        </Sider>
    )
  }
}

export default withRouter(SideMenu);

@Nadun 的解决方案适用于不包含参数的路径。但是,如果您像我一样在路由中使用参数,那么这里有一个适用于任何路由路径的解决方案,包括 /users/:id/users/:id/whatever/:otherId 等疯狂的东西。它使用 react-router 的 matchPath API,它使用与 Router 组件完全相同的逻辑。

// file with routes
export const ROUTE_KEYS = {
    ROOT: "/",
    USER_DETAIL: "/users/:id",
};

export const ROUTES = {
    ROOT: {
        component: Home,
        exact: true,
        key: ROUTE_KEYS.ROOT,
        path: ROUTE_KEYS.ROOT,
    },
    USER_DETAIL: {
        component: Users,
        key: ROUTE_KEYS.USER_DETAIL,
        path: ROUTE_KEYS.USER_DETAIL,
    },
};

.

// place within the App component
<Router>
    <Layout>
        <MyMenu />
        <Layout>
            <Layout.Content>
                {Object.values(ROUTES).map((route) => (
                    <Route {...route} />
                ))}
            </Layout.Content>
        </Layout>
    </Layout>
</Router>

.

// MyMenu component
const getMatchedKey = (location) =>
    (
        Object.values(ROUTES).find((route) =>
            matchPath(location.pathname, route)
        ) || {}
    ).path;

const MyMenu = ({ location }) => {
    return (
        <Layout.Sider>
            <AntMenu mode="inline" selectedKeys={[getMatchedKey(location)]}>
                <AntMenu.SubMenu
                    title={
                        <React.Fragment>
                            <Icon type="appstore" />
                            Home
                        </React.Fragment>
                    }
                >
                    <AntMenu.Item key={ROUTE_KEYS.ROOT}>
                        <Icon type="appstore" />
                        <span className="nav-text">
                            Some subitem
                        </span>
                    </AntMenu.Item>
                </AntMenu.SubMenu>
                <AntMenu.SubMenu
                    title={
                        <React.Fragment>
                            <Icon type="user" />
                            Users
                        </React.Fragment>
                    }
                >
                    <AntMenu.Item key={ROUTE_KEYS.USER_DETAIL}>
                        <Icon type="user" />
                        <span className="nav-text">
                            User detail
                        </span>
                    </AntMenu.Item>
                </AntMenu.SubMenu>
            </AntMenu>
        </Layout.Sider>
    );
};

export default withRouter(MyMenu);

您可以将 link 的路径设置为每个 Menu.Item 上的键。然后 selectedKeys={this.props.location.pathname}

<Menu
  theme="light"
  mode='inline'
  selectedKeys={[this.props.location.pathname,]}
>
  <Menu.Item key={item.path} style={{float:'right'}}>
    Link to={item.path}>{item.name}</Link>
  </Menu.Item>
  {menulist}
</Menu>

项目将根据当前路径设置为活动状态。 我添加了 [] 和尾随逗号,因为 selectedKeys 接受数组,而 this.props.location.pathname 是一个字符串。我只是出于爱好编码,所以不知道是否可以接受。

以下答案假定您使用挂钩。我知道你不在你的问题中,但它可能对其他人有用。此外,如果您有 /banner/this/is/nested 等嵌套路径,此解决方案将有效,并且不仅在按下后退和前进按钮时有效,而且在刷新当前页面时有效:

import React, { useState, useEffect } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import { Layout, Menu } from 'antd'

const { Sider } = Layout

const items = [
  { key: '1', label: 'Invoices', path: '/admin/invoices' },
  { key: '2', label: 'Service Details', path: '/admin/service-details' },
  { key: '3', label: 'Service Contract Details', path: '/admin/service-contract-details' },
  { key: '4', label: 'Cost Centers', path: '/admin/cost-centers' },
  { key: '5', label: 'Clients', path: '/admin/clients' },
  { key: '6', label: 'Vendors', path: '/admin/vendors' }
]

const Sidebar = () => {
  const location = useLocation()
  const history = useHistory()
  const [selectedKey, setSelectedKey] = useState(items.find(_item => location.pathname.startsWith(_item.path)).key)

  const onClickMenu = (item) => {
    const clicked = items.find(_item => _item.key === item.key)
    history.push(clicked.path)
  }

  useEffect(() => {
    setSelectedKey(items.find(_item => location.pathname.startsWith(_item.path)).key)
  }, [location])

  return (
    <Sider style={{ backgroundColor: 'white' }}>
      <h3 style={{ paddingLeft: '1rem', paddingTop: '1rem', fontSize: '1.25rem', fontWeight: 'bold', minHeight: 64, margin: 0 }}>
        Costek
      </h3>
      <Menu selectedKeys={[selectedKey]} mode='inline' onClick={onClickMenu}>
        {items.map((item) => (
          <Menu.Item key={item.key}>{item.label}</Menu.Item>
        ))}
      </Menu>
    </Sider>
  )
}

export default Sidebar

边栏将如下所示:

如果您使用数组并在其上映射(如我的情况)来设置菜单项,它们的顺序必须与它们在侧边菜单中出现的顺序相同,否则,活动栏或背景将不会显示显示。