最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Bold active menu after refreshing the page - Stack Overflow

programmeradmin3浏览0评论

I have an application has been written with React + Redux and Antdesign. My application is a dashboard app. So I used the layout in Ant design /ponents/layout/

When I click on side menus, the active menu gets bold which is fine. But I need when I refresh the page, it checks and detect the route and bold related menu item.

I have a Sidebar ponent which is stateful. Inside it, in ponentDidMount I call a function which will dispatch an action from mapDispatchToProps. The reducer changes the state. But in HTML codes, in defaultSelectedKeys, I can not set the number of active menus.

Sidebar.js ponent:

import React from 'react';
import { render } from 'react-dom';
import { connect } from 'react-redux'
import { Switch, BrowserRouter, Route, Link } from 'react-router-dom';

// antd
import { Layout, Breadcrumb, Menu, Icon } from 'antd';
const { Header, Content, Footer, Sider } = Layout;

// Helpers
import { Alert } from '../helpers/notifications';

// Components
import Home from '../ponents/Home';
// import Header from '../ponents/Header';
import NotFound from '../ponents/NotFound';
import PostsEditor from '../ponents/Posts/PostsEditor';

// Actions
import { setRouteActiveFlag } from '../actions/ui.action'

class Sidebar extends React.Component {

    ponentDidMount () {
      const routes = {
        '/'       : 1,
        '/posts'  : 2,
        '/logout' : 3
      }

      this.props.detectActiveRoute(setRouteActiveFlag({
        routes:routes, 
        path:window.location.pathname
      }))
    }


    render() {

        const { selectedRoute } = this.props;
        console.log(selectedRoute);

        return (
            <div>
              <Layout>
                <Sider
                  style={{
                    overflow: 'auto',
                    height: '100vh',
                    position: 'fixed',
                    left: 0,
                  }} 
                  breakpoint="lg"
                  collapsedWidth="0"
                  onBreakpoint={broken => {
                    console.log(broken);
                  }}
                  onCollapse={(collapsed, type) => {
                    console.log(collapsed, type);
                  }}
                >
                  <div className="logo" >
                    Logo <br/><br/><br/>
                  </div>
                  <Menu theme="dark" mode="inline"  style={{ lineHeight: '64px' }} defaultSelectedKeys={[selectedRoute.toString() || '1']}>
                    <Menu.Item key="1">
                      <Link to="/" style={{ color:'#fff' }}>
                        <Icon type="user" />
                        <span className="nav-text">Home</span>
                      </Link>
                    </Menu.Item>
                    <Menu.Item key="2">
                      <Link to="/posts" style={{ color:'#fff' }}>
                        <Icon type="user" />
                        <span className="nav-text">Posts</span>
                      </Link>
                    </Menu.Item>
                    <Menu.Item key="3">
                      <a href="/logout" style={{ color:'#fff' }}>
                        <Icon type="user" />
                        <span className="nav-text">Logout</span>
                      </a>
                    </Menu.Item>
                  </Menu>
                </Sider>
                <Layout style={{ marginLeft: 200 }}>
                  <Content style={{ margin: '24px 16px 0', overflow: 'initial'}}>

                      <Breadcrumb style={{ margin: '0 0 20px 0' }}>
                        <Breadcrumb.Item>Home</Breadcrumb.Item>
                        <Breadcrumb.Item>List</Breadcrumb.Item>
                        <Breadcrumb.Item>App</Breadcrumb.Item>
                      </Breadcrumb>

                      <div style={{ padding: 24, background: '#fff', minHeight: 360 }}>
                        <Switch>
                            <Route path="/" exact ponent={Home} />
                            <Route path="/posts/:id?" ponent={PostsEditor} />
                            <Route ponent={NotFound}/>
                        </Switch>
                        <Alert stack={ { limit: 3 } } />
                      </div>

                  </Content>

                  <Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
                </Layout>
              </Layout>
            </div>
        );
    }
}

const mapStateToProps = (state, ownProps) => {
    return {
      state: state,
      props: ownProps,
      selectedRoute:state.ui.selectedRoute || 1
    }
}

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
      detectActiveRoute: (obj) => dispatch(obj)
    }
}


export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Sidebar)

ui.action.js

export const setRouteActiveFlag = (payload = 'global') => ({
  type: actions.SET_ROUTE_ACTIVE_FLAG,
  payload
});

ui.reducer.js

import { handleActions } from 'redux-actions';
import Immutable from 'seamless-immutable';
import * as actions from '../consts/action-types';


const initialState = Immutable({
  requests: {},
  selectedRoute:{}
});


export default handleActions({
  [actions.SET_ROUTE_ACTIVE_FLAG]: (state, action) => {
    if (action.payload.routes && action.payload.path && action.payload.routes[ action.payload.path ]) {
        return state.set('selectedRoute', action.payload.routes[ action.payload.path ])
    }else{
        return state.set('selectedRoute', 1)
    }
  }
}, initialState);



Please help me find the best and simple practices.

I have an application has been written with React + Redux and Antdesign. My application is a dashboard app. So I used the layout in Ant design https://ant.design/ponents/layout/

When I click on side menus, the active menu gets bold which is fine. But I need when I refresh the page, it checks and detect the route and bold related menu item.

I have a Sidebar ponent which is stateful. Inside it, in ponentDidMount I call a function which will dispatch an action from mapDispatchToProps. The reducer changes the state. But in HTML codes, in defaultSelectedKeys, I can not set the number of active menus.

Sidebar.js ponent:

import React from 'react';
import { render } from 'react-dom';
import { connect } from 'react-redux'
import { Switch, BrowserRouter, Route, Link } from 'react-router-dom';

// antd
import { Layout, Breadcrumb, Menu, Icon } from 'antd';
const { Header, Content, Footer, Sider } = Layout;

// Helpers
import { Alert } from '../helpers/notifications';

// Components
import Home from '../ponents/Home';
// import Header from '../ponents/Header';
import NotFound from '../ponents/NotFound';
import PostsEditor from '../ponents/Posts/PostsEditor';

// Actions
import { setRouteActiveFlag } from '../actions/ui.action'

class Sidebar extends React.Component {

    ponentDidMount () {
      const routes = {
        '/'       : 1,
        '/posts'  : 2,
        '/logout' : 3
      }

      this.props.detectActiveRoute(setRouteActiveFlag({
        routes:routes, 
        path:window.location.pathname
      }))
    }


    render() {

        const { selectedRoute } = this.props;
        console.log(selectedRoute);

        return (
            <div>
              <Layout>
                <Sider
                  style={{
                    overflow: 'auto',
                    height: '100vh',
                    position: 'fixed',
                    left: 0,
                  }} 
                  breakpoint="lg"
                  collapsedWidth="0"
                  onBreakpoint={broken => {
                    console.log(broken);
                  }}
                  onCollapse={(collapsed, type) => {
                    console.log(collapsed, type);
                  }}
                >
                  <div className="logo" >
                    Logo <br/><br/><br/>
                  </div>
                  <Menu theme="dark" mode="inline"  style={{ lineHeight: '64px' }} defaultSelectedKeys={[selectedRoute.toString() || '1']}>
                    <Menu.Item key="1">
                      <Link to="/" style={{ color:'#fff' }}>
                        <Icon type="user" />
                        <span className="nav-text">Home</span>
                      </Link>
                    </Menu.Item>
                    <Menu.Item key="2">
                      <Link to="/posts" style={{ color:'#fff' }}>
                        <Icon type="user" />
                        <span className="nav-text">Posts</span>
                      </Link>
                    </Menu.Item>
                    <Menu.Item key="3">
                      <a href="/logout" style={{ color:'#fff' }}>
                        <Icon type="user" />
                        <span className="nav-text">Logout</span>
                      </a>
                    </Menu.Item>
                  </Menu>
                </Sider>
                <Layout style={{ marginLeft: 200 }}>
                  <Content style={{ margin: '24px 16px 0', overflow: 'initial'}}>

                      <Breadcrumb style={{ margin: '0 0 20px 0' }}>
                        <Breadcrumb.Item>Home</Breadcrumb.Item>
                        <Breadcrumb.Item>List</Breadcrumb.Item>
                        <Breadcrumb.Item>App</Breadcrumb.Item>
                      </Breadcrumb>

                      <div style={{ padding: 24, background: '#fff', minHeight: 360 }}>
                        <Switch>
                            <Route path="/" exact ponent={Home} />
                            <Route path="/posts/:id?" ponent={PostsEditor} />
                            <Route ponent={NotFound}/>
                        </Switch>
                        <Alert stack={ { limit: 3 } } />
                      </div>

                  </Content>

                  <Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
                </Layout>
              </Layout>
            </div>
        );
    }
}

const mapStateToProps = (state, ownProps) => {
    return {
      state: state,
      props: ownProps,
      selectedRoute:state.ui.selectedRoute || 1
    }
}

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
      detectActiveRoute: (obj) => dispatch(obj)
    }
}


export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Sidebar)

ui.action.js

export const setRouteActiveFlag = (payload = 'global') => ({
  type: actions.SET_ROUTE_ACTIVE_FLAG,
  payload
});

ui.reducer.js

import { handleActions } from 'redux-actions';
import Immutable from 'seamless-immutable';
import * as actions from '../consts/action-types';


const initialState = Immutable({
  requests: {},
  selectedRoute:{}
});


export default handleActions({
  [actions.SET_ROUTE_ACTIVE_FLAG]: (state, action) => {
    if (action.payload.routes && action.payload.path && action.payload.routes[ action.payload.path ]) {
        return state.set('selectedRoute', action.payload.routes[ action.payload.path ])
    }else{
        return state.set('selectedRoute', 1)
    }
  }
}, initialState);



Please help me find the best and simple practices.

Share Improve this question edited Jun 24, 2019 at 11:42 Abderrahim Soubai-Elidrisi 5,2021 gold badge30 silver badges40 bronze badges asked Jun 24, 2019 at 11:14 Abdol SeedAbdol Seed 1,4271 gold badge15 silver badges23 bronze badges 3
  • Upload to sandbox codesandbox.io/s/new – Dennis Vash Commented Jun 24, 2019 at 11:19
  • codesandbox.io/s/gifted-frog-ljffw?from-embed but it still have error – Abdol Seed Commented Jun 24, 2019 at 11:39
  • @SamiAlMorshedi, I added an answer that works when using hooks. – lmiguelvargasf Commented May 26, 2020 at 21:05
Add a ment  | 

3 Answers 3

Reset to default 9

There is no need to use redux, just use react-router to get current pathname and pass it to defaultSelectedKeys.

<Menu defaultSelectedKeys={[this.props.location.pathname]}>
  ...
  .....
</Menu>

Look at this answer , if you don't know how to get pathname

The following answer assumes you are using hooks. I know that from your question you are not using hooks, but it could be useful for other people. This answer works not only when refreshing but also when pressing the back and forward buttons:

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

Your sidebar will look as follows:

You can add any css in your menu by conditioning and adding a class just in this way.

<MenuItem className={ (this.props.location.pathname==='/yourRoute')? 'active' : '' } >

</MenuItem>

In case if you get any kind of undefined error then you can use the 'withRouter' HOC

in this way.

In your ponent where you want to get that location prop, you will first import

import {withRouter} from 'react-router-dom';

then you can export it in this way.

export default withRouter(YourComponent);

Final code can look somewhat similar to this

import React, {Fragment, Component} from 'react';
import {withRouter, Link } from 'react-router-dom';

class Menu extends Component {

   render(){

     const {pathname} = this.props.location;

     return (
        <Fragment>
            <div id="sidebar-menu" className="sidebar-menu">

                    <ul>
                        <li className={(pathname==='/dashboard' || pathname==='/')?'active':''}> 
                            <Link to="/dashboard">Dashboard</Link>
                        </li>

                        <li className={(pathname==='/properties')?'active':''}> 
                            <Link to="/properties">Properties</Link>
                        </li>
                    </ul>

            </div>
        </Fragment>
     );

     }

}
export default withRouter(Menu);
发布评论

评论列表(0)

  1. 暂无评论