Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to use sider with react-router links ? #6576

Closed
xyzdata opened this issue Jun 22, 2017 · 34 comments
Closed

how to use sider with react-router links ? #6576

xyzdata opened this issue Jun 22, 2017 · 34 comments
Labels

Comments

@xyzdata
Copy link

xyzdata commented Jun 22, 2017

https://ant.design/components/menu-cn/#components-menu-demo-sider-current

how to use sidercomponent with react-router links ?

I just can't understand why it doesn't works!

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import logo from './logo.svg';
import './App.css';

import {
    BrowserRouter as Router,
    Route,
    Link
} from 'react-router-dom';

import Item1 from './components/Item1.js';
import Item2 from './components/Item2.js';
import Item3 from './components/Item3.js';


import {Menu, Icon} from 'antd';

import 'antd/dist/antd.css';

const SubMenu = Menu.SubMenu;


const routes = [
    {
        path: '/',
        exact: true,
        sidebar: () => <div>item1</div>,
        main: () => <div><Item1 /></div>
    },
    {
        path: '/item2',
        sidebar: () => <div>item2</div>,
        main: () => <div><Item2 /></div>
    },
    {
        path: '/item3',
        sidebar: () => <div>item3</div>,
        main: () => <div><Item3 /></div>
    }
]

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            message: props.message,
            styles: props.styles,
            Any: props.any,
            width: props.width,
            theme: 'dark',
            current: '1'
        };
        this.handleMenuClick = this.handleMenuClick.bind(this);
        this.handleClick = this.handleClick.bind(this);
    };
    handleClick(e) {
        console.log('click ', e);
        this.setState({
            current: e.key,
        });
    };
    // ES7 property initializer syntax
    handleMenuClick = (e) => {
        // e.preventDefault();
        // e.stopPropagation();
        // e.nativeEvent.stopImmediatePropagation();
        console.log('this is:', this);
        console.log("clicked === \n", e);
        if(this.state.styles === "App-SideBox-init"){
            this.setState({
                message: "e.key",
                styles: "App-SideBox-New",
                width: "width: 40px;"
            });
        }
        if(this.state.styles === "App-SideBox-New"){
            this.setState({
                message: "Hello!",
                styles: "App-SideBox-init",
                width: "width: 300px;"
            });
        }
        console.log("this.state.message === ", this.state.message);
        console.log("this.state.styles === ", this.state.styles);
    };
    componentDidMount() {
        /*window.addEventListener('scroll', this.onScroll.bind(this), false);*/
        // window.removeEventListener('click', this.handleMenuClick.bind(this), false);
        // window.removeEventListener('click', this.handleClick.bind(this), false);
    };
    render() {
        return (
            <div className="App">
                <div className="App-header">
                    <img src={logo} className="App-logo" alt="logo" style={this.props.width}/>
                    <h2>Welcome to React</h2>
                </div>
                <div className="App-SideBox">
                    <div className={this.state.styles}>
                        <Router>
                                <div>
                                    <div style={{ display: 'flex' }}>
                                        <div style={{
                                                padding: '10px',
                                                width: '30%',
                                                background: '#f0f0f0'
                                            }}>
                                            <div className="SideBox-body" style={{ display: 'flex' }}>
                                                <Menu
                                                        theme={this.state.theme}
                                                        onClick={this.handleClick}
                                                        style={{ width: 240 }}
                                                        defaultOpenKeys={['sub1']}
                                                        selectedKeys={[this.state.current]}
                                                        mode="inline"
                                                    >
                                                    <SubMenu
                                                            key="sub1"
                                                            title={
                                                                <span>
                                                                    <Icon type="mail" />
                                                                    <span>Navigation One</span>
                                                                </span>
                                                            }
                                                        >
                                                        <Menu.Item key="1">
                                                            <Link to="/"> item1</Link>
                                                        </Menu.Item>
                                                        <Menu.Item key="2">
                                                            <Link to="/item2">item2</Link>
                                                        </Menu.Item>
                                                        <Menu.Item key="3">
                                                            <Link to="/item3">item3</Link>
                                                        </Menu.Item>
                                                    </SubMenu>
                                                </Menu>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </Router>
                    </div>
                    {/*onClick={this.handleMenuClick}*/}
                    <div onClick={this.handleMenuClick} className="App-SideBox-btn">
                        <span>icon</span>
                    </div>
                </div>
                <div className="App-body">
                    <Router>
                        <div>
                            <div>
                                <div style={{ flex: 1, padding: '10px' }}>
                                    {
                                        routes.map((route, index) => (
                                            <Route
                                                key={index}
                                                path={route.path}
                                                exact={route.exact}
                                                component={route.main}
                                            />
                                        ))
                                    }
                                </div>
                            </div>
                        </div>
                    </Router>
                </div>
            </div>
        );
    }
};

App.defaultProps = {
    message: 'Hello!',
    styles: 'App-SideBox-init'
};

App.propTypes = {
    message: PropTypes.string.isRequired,
    styles: PropTypes.string.isRequired,
    width: PropTypes.string
};

export default App;
@ant-design-bot
Copy link
Contributor

Hello @xgqfrms-gildata, your issue has been closed because it does not conform to our issue requirements. Please use the Issue Helper to create an issue, thank you!

@xyzdata
Copy link
Author

xyzdata commented Jun 22, 2017

  1. click no effect, it should link to item3!

error-1

  1. remove document's click event

error-2

  1. after that, it works!

error-3

what's wrong with this ?

@xyzdata
Copy link
Author

xyzdata commented Jun 22, 2017

I will just give up you!

@afc163
Copy link
Member

afc163 commented Jun 23, 2017

https://github.com/zuiidea/antd-admin

@xyzdata
Copy link
Author

xyzdata commented Jul 21, 2017

#6834 (comment)

@yunshuipiao
Copy link

yunshuipiao commented Dec 21, 2017

you can add Link in Menu.Item。

const MySider = () => {
    return (
        <Sider
            style={{background: "#fff"}}
            breakpoint="lg"
            collapsedWidth="0"
            onCollapse={(collapsed, type) => {
                console.log(collapsed, type)
            }}
        >
            <Menu theme="light" mode="inline" defaultSelectedKeys={["1"]}>
                <Menu.Item key="1">
                    <Link to="/" className="nav-text">A</Link>
                </Menu.Item>
                <Menu.Item key="2">
                    <Link to="/upload" className="nav-text">B</Link>
                </Menu.Item>
                <Menu.Item key="3">
                    <Link to="/c" className="nav-text">C</Link>
                </Menu.Item>
                <Menu.Item key="4">
                    <Link to="/d" className="nav-text">D</Link>
                </Menu.Item>
            </Menu>
        </Sider>
    );
}

@hllinc
Copy link

hllinc commented Apr 2, 2018

then there is no menu state change when click the brower's refresh or back button.

@jkyletreman
Copy link

Im not sure why @yunshuipiao has so many downvotes, his solution fixed my problem. I also came from #4853 and wasn't sure that Menu/Menu.Item could take any HOC.

@AlxGolubev
Copy link

AlxGolubev commented Apr 25, 2018

Thats works for me

@jkyletreman, @hllinc

import React from "react";
import { NavLink } from "react-router-dom";
import { Layout, Menu } from "antd";
import { connect } from "react-redux";
import { logout } from "../../store/actions/auth";
const { Content, Sider } = Layout;

const SystemLayout = props => {
    return (
        <Layout
            style={{
                height: "-webkit-fill-available"
            }}
        >
            <Layout>
                <Sider width={200} style={{ background: "#fff" }} collapsible>
                    <Menu
                        activeKey={props.currentPath}
                        mode="inline"
                        selectedKeys={props.currentPath}
                        style={{ height: "100%", borderRight: 0 }}
                    >
                        <Menu.Item key="/dashboard">
                            <NavLink to="/dashboard" className="nav-text">
                                Dashboard
                            </NavLink>
                        </Menu.Item>
                        <Menu.Item key="/tenants">
                            <NavLink to="/tenants" className="nav-text">
                                Tenants
                            </NavLink>
                        </Menu.Item>
                        <Menu.Item key="/users">
                            <NavLink to="/users" className="nav-text">
                                Users
                            </NavLink>
                        </Menu.Item>
                    </Menu>
                </Sider>
                <Layout style={{ padding: "0 24px 24px" }}>
                    <Content
                        style={{
                            background: "#fff",
                            padding: 24,
                            margin: 0,
                            minHeight: 280
                        }}
                    >
                        {props.children}
                    </Content>
                </Layout>
            </Layout>
        </Layout>
    );
};

const mapStateToProps = state => {
    return {
        currentPath: state.routing.location.pathname
    };
};

const mapDispatchToProps = dispatch => {
    return {
        logout: () => dispatch(logout(dispatch))
    };
};

export default connect(mapStateToProps, mapDispatchToProps)(SystemLayout);

@iwarner
Copy link

iwarner commented May 28, 2018

I use this with latest version of Next and AntD

      <Menu.Item key='home'>
        <Link href='/'>
          <a>
            <Icon type='home' />
            <span className='nav-text'>Home</span>
          </a>
        </Link>
      </Menu.Item>

@VesperDev
Copy link

check this Example

@Dennis-Smurf
Copy link

You can use the selectedKeys from antd's menu and match the location.pathname property of react-router@4 to the menu.item key

import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import { Menu } from 'antd';

const Linkmenu = withRouter(props => {
  const { location } = props;
  return (
    <Menu mode="inline" selectedKeys={[location.pathname]}>
      <Menu.Item key="/">
        <Link to="/">Home </Link>
      </Menu.Item>
      <Menu.Item key="/about">
        <Link to="/about">About</Link>
      </Menu.Item>
    </Menu>
  );
});

export default Linkmenu;

@andrewheekin
Copy link

@Dennis-Smurf great solution

@VictorChen
Copy link

anyone know a good way to default expand the submenu that contains the current active item? eg:

<Menu mode="inline" selectedKeys={[location.pathname]}>
  <SubMenu key="sub1" title={<span><Icon type="mail" /><span>One</span></span>}>
    <Menu.Item>
      <Link to="/foo1">Foo 1</Link>
    </Menu.Item>
    <Menu.Item>
      <Link to="/bar1">Bar 1</Link>
    </Menu.Item>
  </SubMenu>
  <SubMenu key="sub2" title={<span><Icon type="desktop" /><span>Two</span></span>}>
    <Menu.Item>
      <Link to="/foo2">Foo 2</Link>
    </Menu.Item>
    <Menu.Item>
      <Link to="/bar2">Bar 2</Link>
    </Menu.Item>
  </SubMenu>
</Menu>

for /foo2, I'd expect the second submenu to expand...

@malaratn
Copy link

@Dennis-Smurf Thanks.

@jankalfus
Copy link

@Dennis-Smurf What if the route key contains an argument? For example "/users/:id". I think it needs a more complex logic than that.

@jankalfus
Copy link

jankalfus commented Sep 18, 2018

Ok, I came up with a solution that uses react-router's matchPath. It should work for any route path, including /users/:id or crazy stuff like /users/:id/whatever/:otherId, as it uses the exact same logic as the Router component.

Here's a full example:

// 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: UserDetail,
        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);

@robabby
Copy link

robabby commented Oct 13, 2018

I am using @Dennis-Smurf's solution, and it's technically working, but the page wont navigate or load the new component until I move the mouse off of the nav item I clicked on. Anyone know why that could be?

    let { pathname } = this.props.location;

    <Menu
      theme="dark"
      mode="inline"
      selectedKeys={[pathname]}
      defaultOpenKeys={['user-menu']}
    >
      <SubMenu
        key="user-menu"
        title={
          <Link to={`/profile/${user._id}`}>
            <Avatar
              className="menu-avatar"
              src={getAvatarUrl(user && user.local && user.local.email)}
              size={this.props.collapsed ? 16 : 24}
            />
            {!this.props.collapsed && <span>{user.username}</span>}
          </Link>
        }
      >
        <Menu.Item key="/dashboard">
          <Link to="/dashboard">
            <Icon type="dashboard" theme="outlined" />
            <span>Dashboard</span>
          </Link>
        </Menu.Item>
        <Menu.Item key="/calendar">
          <Link to="/calendar">
            <Icon type="calendar" theme="outlined" />
            <span>Calendar</span>
          </Link>
        </Menu.Item>
        <Menu.Item key="/listing/new">
          <Link to="/listing/new">
            <Icon type="plus" theme="outlined" />
            <span>New Listing</span>
          </Link>
        </Menu.Item>
        <Menu.Item key="/settings">
          <Link to="/settings">
            <Icon type="setting" theme="outlined" />
            <span>Settings</span>
          </Link>
        </Menu.Item>
      </SubMenu>
    </Menu> 

@pitops
Copy link

pitops commented Jan 24, 2019

Using one of the solutions above but with @reach/router you can do the following

import {Location} from '@reach/router'

const navButtons = [
  {
    path: '/home',
    name: 'Home'
  }
]

export default function DashboardSideNav({collapsed}) {
  return (
    <Location>
      {({location}) => {
        return (
            <Sider trigger={null} collapsible collapsed={collapsed}>
              <Menu
                theme="dark"
                mode="inline"
                defaultSelectedKeys={[location.pathname]}
              >
                {navButtons.map((button, index) => (
                  <Menu.Item key={button.path}>
                    <ExactNavLink to={button.path}>
                      <Icon type={button.icon} />
                      <span>{button.name}</span>
                    </ExactNavLink>
                  </Menu.Item>
                ))}
              </Menu>
            </Sider>
        )
      }}
    </Location>
  )
}

@dananw
Copy link

dananw commented Feb 26, 2019

i use this and work.

import React, { Component } from 'react';
import { Link, withRouter } from 'react-router-dom';
import {
  Layout, Menu, Icon
} from 'antd';

const {Sider} = Layout;
const SubMenu = Menu.SubMenu;

class Navigation extends Component {
  state = {
    collapsed: false,
    current: '/home',
  };

  onCollapse = (collapsed) => {
    this.setState({ collapsed });
  }

  render() {
    const { location } = this.props
    return (
        <Sider collapsible collapsed={this.state.collapsed} onCollapse={this.onCollapse}>
          <div className="app-logo" />
          <Menu theme="dark" mode="inline" inlineCollapsed={true} selectedKeys={[location.pathname]}>
            <Menu.Item key="/home">
              <Link to="/home">
                <Icon type="pie-chart"/><span>Dashboard</span>
              </Link>
            </Menu.Item>
            <SubMenu key="sub-master" title={<span><Icon type="plus-square"/><span>Master</span></span>}>
              <Menu.Item key="/server">
                <Link to="/server">Server</Link>
              </Menu.Item>
              <Menu.Item key="/company">
                <Link to="/company">Company</Link>
              </Menu.Item>
              <Menu.Item key="/application">
                <Link to="/application">Application</Link>
              </Menu.Item>
              <Menu.Item key="/group">
                <Link to="/group">Group</Link>
              </Menu.Item>
            </SubMenu>
          </Menu>
        </Sider>
    );
  }
}

export default withRouter(Navigation)

@JonDum
Copy link

JonDum commented Apr 3, 2019

If anyone's interested, here's a complete example. It even opens any submenus and highlights the nested submenu correctly on page load and works with deeply nested subroutes by also including the "root" route as one of the selectedKeys

import React from 'react'
import {observer, inject} from 'mobx-react'
import {NavLink, withRouter} from 'react-router-dom'
import {Layout, Menu, Icon} from 'ui'
import {Logo} from 'components'
import {Plane as PlaneIcon, Users as UsersIcon} from 'ui/icons'

const {Sider} = Layout

const Item = ({to, children, exact = false, ...props}) => (
	<Menu.Item {...props}>
		<NavLink class="nav-text" activeClassName="active" to={to} exact={exact}>
			{children}
		</NavLink>
	</Menu.Item>
)

const items = [
	{
		title: 'Dashboard',
		icon: 'appstore-o',
		path: '/',
		exact: true
	},
	{
		title: 'Customize Icon',
		icon: {component: PlaneIcon, style: {width: 19}},
		path: '/test'
	},
	{
		title: 'Submenu Example',
		icon: 'layout',
		path: '/submenu',
		submenu: [
			{
				title: 'Submenu',
				path: '/submenu/one'
			},
			{
				title: 'Thing',
				path: '/submenu/thing'
			},
			{
				title: 'Optimizer',
				path: '/submenu/optimize'
			}
		]
	},
	{
		title: 'Admin',
		icon: 'reconciliation',
		path: '/admin/company'
	}
]

const renderMenuItemChildren = ({icon, title}) => (
	<>
		{icon && <Icon type={icon} {...icon} />}
		<span data-testid={title.toLowerCase + '-nav'}>{title}</span>
	</>
)
const renderMenuItem = item => {
	return item.submenu ? (
		<Menu.SubMenu key={item.path} title={renderMenuItemChildren(item)}>
			{item.submenu.map(renderMenuItem)}
		</Menu.SubMenu>
	) : (
		<Item key={item.path} to={item.path} exact={item.exact}>
			{item.children ? item.children : renderMenuItemChildren(item)}
		</Item>
	)
}

@inject('appstore')
@withRouter
@observer
export default class Navigation extends React.Component {

	render() {
		const {appstore, location} = this.props
		const root = '/' + location.pathname.split('/')[1]
		return (
			<Sider
				theme={appstore.ui.theme}
				collapsible
				collapsed={appstore.ui.siderCollapsed}
				onCollapse={collapsed => (appstore.ui.siderCollapsed = collapsed)}>
				<Menu
					theme={appstore.ui.theme}
					mode="inline"
					style={{top: 50}}
					defaultOpenKeys={[root]}
					selectedKeys={[root, location.pathname]}>
					{items.map(renderMenuItem)}
				</Menu>
			</Sider>
		)
	}

}

@untilla
Copy link

untilla commented May 20, 2019

  1. Sometimes not all routes should be showing in side menu, and this config looks like:
    {
        key: 'home',
        component: Home,
        exact: true,
        showInMenu: false,
        path: '/',
        title: 'PACS Monitor',
        subTitle: 'Dashboard',
    },
    {
        key: 'departments',
        component: Departments,
        showInMenu: true,
        path: '/departments',
        title: 'PASC Monitor',
        subTitle: 'Список отделов',
        icon: 'ordered-list',
    },
  1. Also you should control initial state menu - after refresh page for example or when you on route what not showing in menu - you should deselect the sider items, right?
    I do it like this:
   render() {
        const { collapsed, onCollapse, location: { pathname}  } = this.props;
        const route = getRoute(pathname);
        let selected = '-1';
        if (route.showInMenu) {
            if (this.state.selected !== route.key)
                selected = route.key;
            else
                selected = this.state.selected;
        }

...

const getRoute = pathname => {
    return dashboardRoutes.find(route => {
        return route.path === pathname || `${route.path}/` === pathname;
    })
};

...

Then if you refresh the page - the current route stay selected and when you go to another route - it will deselect sider menu.

But maybe somebody have more correct and elegant solution?

@dhamaniasad
Copy link

class Sidemenu extends Component {
  state = { selectedRoute: "home" }


  setActiveRoute() {
    switch (this.props.history.location.pathname) {
      case "/someRoute":
        this.setState({ selectedRoute: "someRoute" })
        break;
      case "/":
        this.setState({ selectedRoute: "home" });
        break;
      default:
        break;
    }
  }

  componentDidMount() {
    // Set initial route on page load 
    this.setActiveRoute();
    this.props.history.listen(() => {
      // Listen for route change and set selected route key for sidebar
      this.setActiveRoute();
    });
  }
  render() {
    return (
      <Menu
        theme="dark"
        mode="inline"
        selectedKeys={[this.state.selectedRoute]}
        style={{ height: '100%', borderRight: 0 }}
      >
        <Menu.Item key="home" />
      </Menu>
    )
  }
}

This is one way to do it.

@diegoleft
Copy link

great solution @Dennis-Smurf this should be in the documentation of Ant.design

@xyombo
Copy link

xyombo commented Mar 4, 2020

@Dennis-Smurf , perfect solution!!thx

@kabinpokhrel
Copy link

ant-design-with-routing

@0104910052
Copy link

Could get simple as that

   <Menu
    theme="dark"
    mode="horizontal"
    defaultSelectedKeys={[history.location.pathname.split('/')[1] || 'dashboard']}
   >
    <Menu.Item key="dashboard" onClick={() => history.push('/')}>
      ΠΙΝΑΚΑΣ
    </Menu.Item>
    <Menu.Item key="settings" onClick={() => history.push('/settings/accounts')}>
      ΡΥΘΜΙΣΕΙΣ
    </Menu.Item>
  </Menu>`

@JohnnyLuv
Copy link

JohnnyLuv commented Jun 3, 2020

try this

import React, { useState, useEffect } from 'react'
import { Switch, Route, Link, Redirect, useHistory } from 'react-router-dom'
import Home from './Home'
import Setting from './Setting'
import Group1Page1 from './group1/Page1'
import Group1Page2 from './group1/Page2'

import '../assets/css/layout.sass'
import { Layout, Menu } from 'antd'
import {
  HomeOutlined,
  AuditOutlined,
  PayCircleOutlined,
  UserOutlined,
} from '@ant-design/icons'
const { Header, Content, Footer, Sider } = Layout
const { SubMenu } = Menu


export default props => {
  const history = useHistory()

  const MenuList = [
    { title: '首页', icon: <HomeOutlined />, path: '/', },
    {
      title: 'group1', icon: <AuditOutlined />,
      children: [
        { title: 'page1', path: '/group1/page1', },
        { title: 'page2', path: '/group1/page2', },
      ]
    },
    { title: '设置', icon: <PayCircleOutlined />, path: '/setting', },
  ]
  const currPath = history.location.pathname
  const [title, setTitle] = useState(null)

  function menuChange(info) {
    if (currPath === info.path) return
    history.push(info.path)
  }

  useEffect(() => {
    for (let item of MenuList) {
      if (!item.children) {
        if (item.path === currPath) setTitle(item.title)
      } else {
        for (let child of item.children) {
          if (child.path === currPath) setTitle(child.title)
        }
      }
    }

  }, [currPath])

  return (
    <Layout>
      <Sider className='layout-sider' >
        <div className="layout-logo">QMPC SYSTEM</div>
        <Menu theme='dark' mode="inline" defaultOpenKeys={['group1']} selectedKeys={[title]}>
          {
            MenuList.map(menu => {
              if (!menu.children) {
                return <Menu.Item key={menu.title} icon={menu.icon} onClick={() => { menuChange(menu) }}>{menu.title}</Menu.Item>
              } else {
                return (
                  <SubMenu key={menu.title} icon={menu.icon} title={menu.title}>
                    {
                      menu.children.map(child => {
                        return <Menu.Item key={child.title} onClick={() => { menuChange(child) }}>{child.title}</Menu.Item>
                      })
                    }
                  </SubMenu>
                )
              }
            })
          }
        </Menu>
      </Sider>

      <Layout className="layout-container">
        <Header className="layout-header">
          <span className="title">{title}</span>
          <div className='account-cover'>
            <UserOutlined className='icon-user' />
            <span className="account">account</span>
            <Link to='/sign-in'>登出</Link>
          </div>
        </Header>
        <Content className='layout-content'>
          <Switch>
            <Route exact path='/' component={Home} />
            <Route exact path='/group1/page1' component={Group1Page1} />
            <Route exact path='/group1/page2' component={Group1Page2} />
            <Route exact path='/Setting' component={Setting} />
            <Redirect to='/404' />
          </Switch>
        </Content>
        <Footer className='layout-footer'>XXX System ©2020 Created by XXX Tech</Footer>
      </Layout>
    </Layout>
  )
}

@BadalSherpa
Copy link

Im not sure why @yunshuipiao has so many downvotes, his solution fixed my problem. I also came from #4853 and wasn't sure that Menu/Menu.Item could take any HOC.

he is getting the down vote because the highlighting of different menu options can't be resolved with this as Link gets refreshed whenever clicked and the default key gets highligted

@bakhrom-akbarov
Copy link

bakhrom-akbarov commented Jan 29, 2021

Hi everyone!

This is what I've discovered:

                <Menu style={{ width: '100%' }} mode="horizontal">
                    <NavLink exact to={`/stores/${this.props.match.params.storeId}`} className='ant-menu-item' activeClassName='ant-menu-item-selected'>Инфо</NavLink>
                    <NavLink to={`/stores/${this.props.match.params.storeId}/branches`} className='ant-menu-item' activeClassName='ant-menu-item-selected'>Филиалы</NavLink>
                </Menu>

As I understood, menu should have wide width in order not to be hidden in "more" like button.
This is horizontal version. It should work for vertical mode too.

@hosseint79
Copy link

Hi
For active menu item and keep items open even after refreshing page base on route you can use this package
https://www.npmjs.com/package/activate-menu-item

@d9k
Copy link

d9k commented Mar 23, 2022

component:

import { useMemo } from 'react';
import { matchPath, NavLink, To } from 'react-router-dom';
import { Menu } from 'antd';

export type MenuItem = {
  key?: string;
  path: string;
  pathMatchPattern?: string;
  caption: string;
};

export type MenuProps = {
  location: any;
  items: MenuItem[];
};

const TopMenu: React.FC<MenuProps> = ({ items, location }) => {
  const selectedKeys = useMemo(
    () =>
      items.reduce<string[]>((acc, item) => {
        if (matchPath(item.pathMatchPattern || item.path, location.pathname)) {
          acc.push(item.key || item.path);
        }
        return acc;
      }, []),
    [items, location]
  );

  return (
    <Menu mode="horizontal" selectedKeys={selectedKeys} theme="dark">
      {items.map((item) => (
        <Menu.Item key={item.key || item.path}>
          <NavLink to={item.path}>{item.caption}</NavLink>
        </Menu.Item>
      ))}
    </Menu>
  );
};

export default TopMenu;

usage:

const location = useLocation();

return (
      <TopMenu
        items={[
          {
            path: '/',
            caption: 'Main',
          },
          {
            path: '/profile',
            caption: 'Profile',
          },
          {
            path: '/orders',
            caption: 'Orders',
          },
          {
            path: '/logout',
            caption: 'Logout',
          },
        ]}
        location={location}
      />
 );

@MaxShylov
Copy link

Create a link and work with the collapse

Active link set selectedKeys

Important use <span> for label

<Menu.Item key={id}>
    <NavLink to={path}> 
        {createElement(icon)}
        <span>{name}</span>
     </NavLink>
 </Menu.Item>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests