I'm developing a single-page ReactJS Web app but I know I'm doing something wrong while defining my routing with React Router V4
.
My problem is as follows: PrivateRoute
in my routes.js
file is not working as expected. That is, I don't know why but my sidebar menu, which is a child component of my main App
, does not work properly: there are a few bugs when I click on the menu item of my PrivateRoute
component (in this case, Page1
). When I say bugs, I mean that when I click on the menu item of a PrivateRoute
, the sidebar menu is misconfigured. The content of the component (Page1
) is rendered but the menu item in the sidebar menu does not stay highlighted as it should be: it is redirected to the first menu item on the list, in this case MainHome
.
Nonetheless, if I change the tag of my Page1
component from PrivateRoute
to ConfigRoute
in my routes.js
file, everything works properly.
A sample from my files index.js
(main), routes.js
, App.js
, and side-menu.js
:
index.js
(main) file:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import Routes from './routes';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<Routes/>
</Provider>,
document.getElementById('root')
);
routes.js
file:
import React, { Fragment } from 'react';
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import Login from './login';
import App from './App';
import MainHome from './home';
import Page1 from './page1';
const ConfigRoute = ({ component: Component, layout: Layout, ...rest }) => (
<Route {...rest} render={(props) => (
<Layout>
<Component {...props}/>
</Layout>
)}/>
);
const PrivateRoute = ({ component: Component, layout: Layout, auth_status, ...rest }) => (
<Route {...rest} render={(props) => (
<Fragment>
{auth_status === true
? (
<Layout>
<Component {...props}/>
</Layout>
)
: (
<Redirect to={{ pathname: "/", state: { from: props.location } }}/>
)
}
</Fragment>
)}/>
);
const Routes = (props) => (
<Router>
<Switch>
<Route exact path="/" component={Login}/>
<ConfigRoute path="/home" layout={App} component={MainHome}/>
<PrivateRoute path="/page1" auth_status={props.auth_status} layout={App} component={Page1}/>
</Switch>
</Router>
);
const mapStatetoProps = state => ({
auth_status: state.auth.isAuthenticated,
});
export default connect(mapStatetoProps)(Routes);
App.js
file
import React, { Component } from 'react';
import { Layout } from 'antd';
import SideMenu from './side-menu';
import MainHeader from './main-header';
import MainFooter from './main-footer';
const { Content } = Layout;
class App extends Component {
constructor(props) {
super(props);
this.state = {
collapsed: false,
};
}
toggle = () => {
this.setState({
collapsed: !this.state.collapsed,
});
}
render() {
return (
<Layout style={{ minHeight: '100vh' }}>
<SideMenu collapsed={this.state.collapsed}/>
<Layout>
<MainHeader triggerParentUpdate={this.toggle} collapsed={this.state.collapsed}/>
<Content style={{ margin: '24px 16px', padding: 24, background: '#ffffff', minHeight: 280 }}>
{this.props.children}
</Content>
<MainFooter/>
</Layout>
</Layout>
);
}
}
export default App;
side-menu.js
file:
import React from 'react';
import { Layout, Menu, Icon } from 'antd';
import { NavLink } from 'react-router-dom';
const { Sider } = Layout;
const SubMenu = Menu.SubMenu;
class SideMenu extends React.Component {
render() {
return (
<Sider
trigger={null}
collapsible
collapsed={this.props.collapsed}
>
<div className="logo"/>
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
<Menu.Item key="1">
<NavLink to="/home">
<Icon type="home"/>
<span>home</span>
</NavLink>
</Menu.Item>
<Menu.Item key="2">
<NavLink to="/page1">
<Icon type="star-o"/>
<span>page 1</span>
</NavLink>
</Menu.Item>
</Menu>
</Sider>
);
}
}
export default SideMenu;
Since React works with only components, React Router v4 is component based. React Router gives us components like Route, Link, Switch, Prompt etc. to work with.
Because React Router allows us to render as many Routes as we'd like, and because we abstracted our routes to their own array, we can render different components at different sections of our page whenever the app's location matches the Route 's path. Want to learn more?
Redirect component’s to prop is used here to tell React Router where to redirect the user. to prop can also be an Object {pathname: ‘/login’, state: {…}} where state is any payload you want to pass to Login component as prop which is accessible from props.location.state inside Login component.
React Router ( react-router-dom) provided BrowserRouter and HashRouter components which is starting point of your application. As we are using BrowserRouter ( path based ), we will import it like below in index.js.
I found one possible solution here - use the path as the menu keys and provide the location as the selectedKeys
:
<Menu mode="inline" selectedKeys={[location.pathname]}>
<Menu.Item key="/home">
<Link to="/home">Home </Link>
</Menu.Item>
...
You can test the solution here
Hope it helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With