jeepers3327
jeepers3327

Reputation: 554

Different component in a same React route based on roles

I want to set different component on a same React route based on user roles.

Here's what I did.

// AppRouter.js
const AppRouter = () => {

    const {loading, data, error}  = useQuery(GET_CURRENT_USER)

    if (loading) return <span>Loading...</span>
    if (error) return <Redirect to="/login"/>

    if (data) {
        let {me: {role}}= data

        if (role === 'user') {
            return (
                    <Switch>
                        <Route to="/" component={EmployeeHome}/>
                        <Route path="/login" exact component={SigninForm} />
                    </Switch>
            )
        } else {
            return (
                    <Switch>
                    <Route to="/" component={AdminHome}/>
                    <Route path="/login" exact component={SigninForm} />
                    </Switch>
            )
        }
    }

}

// App.js
    <Router history={history}>
      <div className="App">
        <header>
          <h3>This is header!</h3>
        </header>
        <Switch>
          <AppRouter />
          <Route path="/login" component={SigninForm} />
        </Switch>
      </div>
    </Router>

  1. Call a graphql query that retrieves the current user based on the authorization token stored in localStorage.
  2. If error, will redirect to /login
  3. If successful, will create a specific routes available for that particular role. 3a. If role is user, it will create a route which has a EmployeeHome component 3b. If role is admin, it will create a route which has a AdminHome component

After trying the the code above, it doesn't load the SigninForm which points to /login route.

What should be done to make this work?

Added: Replicable Codesandbox

Upvotes: 2

Views: 1269

Answers (1)

Ajeet Shah
Ajeet Shah

Reputation: 19843

There is no need to use Switch in AppRouter component unless it has "nested" routes. Also, no need to define /login route more than once.

<Router history={history}>
  // ... header here
  <Switch>
    <Route exact path="/login" component={SigninForm} />
    <AppRouter />
  </Switch>
</Router>

where AppRouter is:

const AppRouter = () => {
  // define loading, error, role etc. 

  if (loading) return <span>Loading...</span>
  if (error) return <Redirect to="/login" />

  if (role === 'user') {
    return (
      <>
        <Route exact path="/" component={EmployeeHome} />
      </>
    )
  } else {
    return (
      <>
        <Route exact path="/" component={AdminHome} />
      </>
    )
  }
}

And, <Redirect to="/login" /> should not be the very first line in your router setup as it will keep redirecting to "/login" without seeing further defined routes, hence won't render anything. Your current setup in AppRouter return <Redirect to="/login" /> when there is an error and in that case, it becomes the very first line which is problematic.

Though, you can write something like this: <Redirect exact from="/" to="/login" />.

Upvotes: 1

Related Questions