Gayan
Gayan

Reputation: 2935

React Router Switch did not work properly

I am new to react and trying to create simple navigation which has two menu items (Dashboard and Users). But when I click on Users link it did not render that page content, but dashboard content getting hide. Someone please help me to resolve the issue.

App.js

import React, { Component } from 'react';
import Login from './pages/Login';
import { BrowserRouter as Router, Switch, Route, Link, Redirect, withRouter } from 'react-router-dom';

import { history } from './_helpers/history';
import { authenticationService } from './_services/authentication.service';
import Users from './pages/users/Users';
import Dashboard from './pages/Dashboard';


class App extends Component {

  constructor(props) {
    super(props);

    this.state = {
        currentUser: null
    };
  }


  componentDidMount() {
      authenticationService.currentUser.subscribe(x => this.setState({ currentUser: x }));
  }

  logout() {
      authenticationService.logout();
      history.push('/login');
  }



  render () { 
    const { currentUser } = this.state;
    return (
      currentUser ? (

        <Router>
        <div id="wrapper">  
           <ul>
        <li><Link to={'/'} className="nav-link" > <i className="fas fa-fw  fa-tachometer-alt"></i> <span>Dashboard</span> </Link></li>
          <li><Link to={'/users'} className="nav-link" > <i className="fas fa-fw fa-users"></i> <span>Users</span> </Link></li>
        </ul>

          <Switch>
              <Route path='/' component={Dashboard} />
              <Route path='/users' component={Users} />
          </Switch>

          </div>



      </Router>
      ) : <Login />
  );
 }
}

export default App;

Dashboard.js

import React, { Component } from 'react';
import { Formik, Field, Form, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { authenticationService } from '../_services/authentication.service';
import { history } from '../_helpers/history';


class Dashboard extends Component {

  constructor (props){
    super(props);

    if (authenticationService.currentUserValue) { 
      history.push('/');
    }

    this.state = {
        isPage: '/'
    }    

  }

    render (){
        if(this.state.isPage == window.location.pathname){
            return (    
                <div className="container">
                    dashboard
                </div>
            )
        }else{
            return '';
        }
    }

  }


export default Dashboard;

Users.js

import React, { Component } from 'react';
import { Formik, Field, Form, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { authenticationService } from '../../_services/authentication.service';
import { history } from '../../_helpers/history';


class Users extends Component {

  constructor (props){
    super(props);

    if (authenticationService.currentUserValue) { 
      history.push('/');
    }

    this.state = {
        isPage: '/users'
    }

  }

    render (){
        if(this.state.isPage == window.location.pathname){
            return (    
                <div className="container">
                    users
                </div>
            )
        }else{
            return '';
        }

    }

  }


export default Users;

Upvotes: 5

Views: 261

Answers (6)

Kishor Patil
Kishor Patil

Reputation: 950

Remove this line.

if (authenticationService.currentUserValue) { 
  history.push('/');
}

this is redirecting you again and again to the same page.

Upvotes: 0

reinis_mx
reinis_mx

Reputation: 1296

According to the documentation, <Switch>

Renders the first child or that matches the location.

In your code you have:

<Route path='/' component={Dashboard} />
<Route path='/users' component={Users} />

The problem is that path='/' actually matches any path, including /users, because /users starts with /. So when the route is /users, the Redirect component renders the Dashboard Route and stops looking for other routes.

To fix this, you could add the exact prop to the / Route. exact means that / will not match anything except paths that are exactly "/":

<Route exact path="/" component={Dashboard} />
<Route path="/users" component={Users} />

Now, if the path is /users, the Dashboard Route no longer matches, and the Switch checks if the next Route matches, which it does!

Fixed & simplified example: https://codesandbox.io/s/lucid-leaf-fkgv9

Note that I have removed some code (like this.state.isPage == window.location.pathname) which seemed to be checking if the route matches. You don't need to worry about this in your components, because React-Router takes care of all the routing for you!

Another solution would be to put the Users Route first so that it is checked first, but this can get messy if you have multiple Routes and want to keep them organized.

Upvotes: 0

Minh Nguyen
Minh Nguyen

Reputation: 1

I assume your ../../_helpers/history looks like that

import { createBrowserHistory } from "history";
export default createBrowserHistory();

You forget to pass history to Router as props, so other components do not know what is history

<Router history={history}>...</Router>`

Upvotes: 0

Naismith
Naismith

Reputation: 266

When using the <Switch> component, it will render the first component (in order) that matches the path. Optionally you can put an exact prop on the route so it must match the path 100%.

Your <Dashboard> component is being rendered, however your logic for returning an empty string if the path does not match is preventing you from seeing it. You can move the <Users> route higher, or put an exact prop on your routes.

I've created a small CodeSandbox

https://codesandbox.io/s/festive-worker-t7ly3

Upvotes: 1

Emeka Augustine
Emeka Augustine

Reputation: 931

In App.js component; make Switch direct child of Router; that will fix the issue. You can refactor your code like so:

<Router>
    <Switch>
        <div id="wrapper">  
            <ul>
                <li><Link to={'/'} className="nav-link" > <i className="fas fa-fw  fa-tachometer-alt"></i> <span>Dashboard</span> </Link></li>
                <li><Link to={'/users'} className="nav-link" > <i className="fas fa-fw fa-users"></i> <span>Users</span> </Link></li>
            </ul>

            <Route path='/' component={Dashboard} />
            <Route path='/users' component={Users} />
        </div>
    </Switch> 
</Router>

Upvotes: 2

cjbt
cjbt

Reputation: 344

but dashboard content getting hide.

Can you elaborate on that? I'm not quite understanding what you mean.

The problem may lie with your use of react lifecycles. authenticationService.currentUser.subscribe() is set on componentDidMount() so only after the JSX gets mounted to the DOM. Your Users component is checking authenticationService.currentUserValue on the constructor which runs first before it gets mounted. authenticationService.currentUserValue maybe giving you a falsy which will kick you out to /. Console log that value or place those inside a componentDidMount so it will only check after the mount.

constructor (props){
    super(props);

    this.state = {
        isPage: '/users'
    }
  }

  componentDidMount() {
     if (authenticationService.currentUserValue) { 
      history.push('/');
    }
  }

Upvotes: 1

Related Questions