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

v2 onEnter with FailureComponent #206

Open
dlong500 opened this issue Sep 27, 2017 · 5 comments
Open

v2 onEnter with FailureComponent #206

dlong500 opened this issue Sep 27, 2017 · 5 comments

Comments

@dlong500
Copy link

I'm trying to migrate from v1 to v2 of redux-auth-wrapper. I'm sticking with RR3 for the moment since I'm using SSR and it will be a larger undertaking to migrate everything to RR4 (though I do want to migrate eventually).

That being said, I'm running into some issues with getting everything working like it was under v1. To summarize, I'm not sure how to get a FailureComponent working under v2 and still support SSR with onEnter, as it appears createOnEnter only supports redirect actions. I might just be overlooking something simple, so I'd love some feedback since there isn't any documentation for precisely my type of scenario.

Here's some of my original v1 code utilizing wrappers for UserIsAuthenticated and UserIsAdmin (note that UserisAuthenticated redirects on failure, while UserIsAdmin displays a FailureComponent:

import React from 'react';
import { Route, IndexRoute } from 'react-router';
import { UserAuthWrapper } from 'redux-auth-wrapper';

import App from './app/base/App';
import NoMatch from './app/base/components/NoMatch';
import NotAuthorized from './app/base/components/NotAuthorized';

// import Auth components
import Login from './app/auth/Login';

// import Public components
import Home from './app/public/Home';

// import User components
import User from './app/user/User';
import DashboardUser from './app/user/DashboardUser';
import ProfileUser from './app/user/ProfileUser';
import StatusUser from './app/user/StatusUser';

// import Admin components
import Admin from './app/admin/Admin';
import DashboardAdmin from './app/admin/dashboard-admin';
import OperationsPanel from './app/admin/OperationsPanel';
import Overview from './app/admin/Overview';

const UserIsAuthenticated = UserAuthWrapper({
  authSelector: state => state.auth.login,
  predicate: login => login.status.isAuthenticated,
  wrapperDisplayName: 'UserIsAuthenticated',
})

const UserIsAdmin = UserAuthWrapper({
  authSelector: state => state.auth.login,
  predicate: login => login.userInfo.role === 9,
  allowRedirectBack: false,
  FailureComponent: NotAuthorized,
  wrapperDisplayName: 'UserIsAdmin',
})

export default (store) => {
  const connect = (fn) => (nextState, replaceState) => fn(store, nextState, replaceState)

  const onEnterChain = (...listOfOnEnters) => (store, nextState, replace) => {
    let redirected = false
    const wrappedReplace = (...args) => {
      replace(...args)
      redirected = true
    };
    listOfOnEnters.forEach((onEnter) => {
      if (!redirected) {
        onEnter(store, nextState, wrappedReplace);
      }
    })
  }

  return (
    <Route path="/" component={App}>
      <IndexRoute name="home" component={Home} show_title={false} />
      <Route name="Login" path="/login" component={Login} show_title={false} />
      <Route name="Admin" path="/admin" component={UserIsAuthenticated(UserIsAdmin(Admin))} onEnter={connect(onEnterChain(UserIsAuthenticated.onEnter, UserIsAdmin.onEnter))} title="Admin Dashboard">
        <Route name="dashboard-admin" component={DashboardAdmin}>
          <IndexRoute component={OperationsPanel} big_header={false} />
          <Route name="admin-operations" path="/admin/operations" component={OperationsPanel} big_header={false} />
          <Route name="admin-overview" path="/admin/overview" component={Overview} big_header={false} />
        </Route>
      </Route>
      <Route name="user" path="/user" component={UserIsAuthenticated(User)} onEnter={connect(UserIsAuthenticated.onEnter)} title="User Dashboard">
        <Route name="dashboard-user" component={DashboardUser}>
          <IndexRoute component={ProfileUser} />
          <Route name="profile-user" path="/user/profile" component={ProfileUser} />
          <Route name="status-user" path="/user/status" component={StatusUser} />
        </Route>
      </Route>
      <Route path="*" component={NoMatch} show_title="false" />
    </Route>
  )
}

And here's what I've got so far with v2, though obviously it will redirect if a user doesn't have admin rights instead of displaying a FailureComponent:

import React from 'react';
import { Route, IndexRoute } from 'react-router';
import connectedAuthWrapper from 'redux-auth-wrapper/connectedAuthWrapper';
import { connectedRouterRedirect, createOnEnter } from 'redux-auth-wrapper/history3/redirect';

import App from './app/base/App';
import NoMatch from './app/base/components/NoMatch';
import NotAuthorized from './app/base/components/NotAuthorized';

// import Auth and Registration components
import Login from './app/auth/Login';

// import Public components
import Home from './app/public/Home';

// import User components
import User from './app/user/User';
import DashboardUser from './app/user/DashboardUser';
import ProfileUser from './app/user/ProfileUser';
import StatusUser from './app/user/StatusUser';

// import Admin components
import Admin from './app/admin/Admin';
import DashboardAdmin from './app/admin/dashboard-admin';
import OperationsPanel from './app/admin/OperationsPanel';
import Overview from './app/admin/Overview';


const onEnterAuth = createOnEnter({
  redirectPath: '/login',
  authenticatedSelector: state => state.auth.login.status.isAuthenticated,
})

const UserIsAuthenticated = connectedRouterRedirect({
  redirectPath: '/login',
  authenticatedSelector: state => state.auth.login.status.isAuthenticated,
  wrapperDisplayName: 'UserIsAuthenticated',
})

const onEnterAdmin = createOnEnter({
  redirectPath: '/login',
  authenticatedSelector: state => state.auth.login.userInfo.role === 9,
})

const UserIsAdmin = connectedAuthWrapper({
  authenticatedSelector: state => state.auth.login.userInfo.role === 9,
  FailureComponent: NotAuthorized,
  wrapperDisplayName: 'UserIsAdmin',
})

export default (store) => {
  const connect = (fn) => (nextState, replaceState) => fn(store, nextState, replaceState)

  const onEnterChain = (...listOfOnEnters) => (store, nextState, replace) => {
    let redirected = false;
    const wrappedReplace = (...args) => {
      replace(...args);
      redirected = true;
    };
    listOfOnEnters.forEach((onEnter) => {
      if (!redirected) {
        onEnter(store, nextState, wrappedReplace);
      }
    });
  };

  return (
    <Route path="/" component={App}>
      <IndexRoute name="home" component={Home} show_title={false} />
      <Route name="Login" path="/login" component={Login} show_title={false} />
      <Route name="Admin" path="/admin" component={UserIsAuthenticated(UserIsAdmin(Admin))} onEnter={connect(onEnterChain(onEnterAuth, onEnterAdmin))} title="Admin Dashboard">
        <Route name="dashboard-admin" component={DashboardAdmin}>
          <IndexRoute component={OperationsPanel} big_header={false} />
          <Route name="admin-operations" path="/admin/operations" component={OperationsPanel} big_header={false} />
          <Route name="admin-overview" path="/admin/overview" component={Overview} big_header={false} />
        </Route>
      </Route>
      <Route name="user" path="/user" component={UserIsAuthenticated(User)} onEnter={connect(onEnterAuth)} title="User Dashboard">
        <Route name="dashboard-user" component={DashboardUser}>
          <IndexRoute component={ProfileUser} />
          <Route name="profile-user" path="/user/profile" component={ProfileUser} />
          <Route name="status-user" path="/user/status" component={StatusUser} />
        </Route>
      </Route>
      <Route path="*" component={NoMatch} show_title="false" />
    </Route>
  )
}
@mjrussell
Copy link
Owner

@dlong500 Sorry for the delay. So in v1, onEnter didn't do anything when you had a FailureComponent set. All of these weird relationships between onEnter and the wrapper is why I pulled onEnter out of HOC and into its own helper. In your new case, you should structure your code exactly as you said, if you don't want to redirect the user then you don't want onEnter. So your /admin route should just be onEnter={connect(onEnterAuth)}. The HOC will display the FailureComponent for Admin when the user isn't an admin instead of redirecting them.

Does that make sense?

@dlong500
Copy link
Author

But if onEnter is not utilized for checking if the user has admin privileges does this mean all the logic happens on the client side? I guess it's not that big of a deal, but for SSR apps it would be nice if aFailureComponent was rendered on the server rather than deferring that check until after the response has been sent to the client. Of course I still could just be confused and not understanding something.

In any case, I'll play around with it soon and let you know if I run into any problems with structuring it how you suggested first before I make things any more complicated.

@mjrussell
Copy link
Owner

Im no SSR expert, but my understanding is the onEnter is used just for redirection, if you pass down a store on the server that causes the Failure component condition to trip then the initial render should still send down the Failure component. (It's still an HOC so both should be there). My main point though is that your previous v1 example didn't use the onEnter for admin either. The onEnter didnt do anything when a failure component was set. So this should keep the existing behavior.

@dlong500
Copy link
Author

I think I understand now. If onEnter is only used for redirection then it makes sense that we'd only need to hook into it for inspections that result in redirection on a failure condition. Obviously there can only either be a failure component displayed or a redirection, but not both at the same time.

In any case, it might be nice to have a bit more documentation/examples to clarify this, as it only became clear to me once I realized that onEnter isn't needed with SSR for actions other than redirection.

@mjrussell
Copy link
Owner

I agree that it would be great to improve the docs, do you want to take a stab at a PR?

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

No branches or pull requests

2 participants