Once login is successful, how to navigate and display nav links

孤人 提交于 2020-06-16 17:30:40

问题


When login is successful, I would like to display the following Navigation Links based on following privileges. Display Profile nav link based on res.data.loginData[0].privilege === "PLAYER". Display Profile, Register nav link if res.data.loginData[0].privilege === "ADMIN".

Without login, when a user access the site, we should be displaying Home, Login and Aboutus pages only.

Being a beginner in this space, I am not sure if it implemented like this, please advise.

Login.js

const handleSubmit = (e) => {
    e.preventDefault()
    const fetchData = async () => {
      try {
        const res = await Axios.post('http://localhost:8000/service/login', { email , password });
        setLoginData(res.data.loginData); 
          if (email === res.data.loginData[0].email && password === res.data.loginData[0].password) {
            setError(false);
            setHelperText('Login Successfully !');
            if(res.data.loginData[0].privilege === "PLAYER"){

            }
        } else {
            setError(true);
            setHelperText('Incorrect Email or Password..!')
          }
      } catch (e) {
        console.log(e);
      }
    }
    fetchData();
  };

Navigation.js

const Navigation = () => {
    return (
        <div className="App">
            <div className="wrapper">
                <nav className="siteNavigation_nav_links">
                    <NavLink className="mobile_register_link" to="/">Home</NavLink>
                    <NavLink className="mobile_register_link" to="/profile">Profile</NavLink>
                    <NavLink className="mobile_register_link" to="/register">Register</NavLink>
                    <NavLink className="mobile_login_link" to="/login">Login</NavLink>
                    <NavLink className="mobile_login_link" to="/aboutus">About us</NavLink>
                </nav>
            </div>
        </div>
    );
}

export default Navigation;

App.js

var ReactDOM = require('react-dom');
const App = () => ( <BrowserRouter>
  <>
    <Navigation />
    <Switch>
      <Route exact path="/" component={Home} />
      <Route path="/profile" component={Profile} />
      <Route path="/register" component={Register} />
      <Route path="/login" component={Login} />
      <Route path="/aboutus" component={Aboutus} />
    </Switch>
  </>
</BrowserRouter>)
ReactDOM.render(React.createElement(App, null), document.getElementById('root'));

export default App;

回答1:


TLDR: Conditionally render your NavLinks in the Navigation component. Checkout the Sandbox.


SOME Context.

@soccerway, Since this question could be answered with the same approach in a previously answered one, for brevity, I recycled this Code Sandbox, with a few minor changes to try and reproduce your case wiht the following assumptions...

  1. It looks like you are using local state with useState on logging in based on this statement setLoginData(res.data.loginData), but since your component might be unmounted by the Navbar given the fact that you are not having another Navbar or a dashboard and your users are bound to be moving back and forth easily, unmounting that component will result in the app loosing that state. It's much better to use higher up state management keeping the Auth and Privilege data between pages. You could use React's Context and access it with useContext hook or use Redux and wrap session data around the entire app. Once the user is logged in, save the app state in context or a store and retrieve it in whichever component needs to have that permission/privilege condition. In my case, I use the context api api, and store the user-id in localStorage.(You can use whatever session storage you want.)

  2. Since I don't have access to your api, I created a simple fake Auth API, and to cover the handleSubmit. In the AuthProvider, I assumed the data you are getting from the server in this line res.data.loginData[0].privilege === "PLAYER" to be of the following format, but it can be anything.

// Sample API FORMAT: Note this is normalized not array like -> loginData[0]
const users = {
  "player-1": {
    id: "player-1",
    username: "Player One",
    // permissions: ["view-profile"], // Alternatively, you could have permission logic
    privilege: "PLAYER" // Fetched by => res.data.loginData[0].privilege === "PLAYER"
  },
  "admin-1": {
    id: "admin-1",
    username: "Admin One",
    // permissions: ["view-profile", "register-user"],
    privilege: "ADMIN"
  }
};

// NOTE: The authenticated user is saved in context as currentUser,
// and the login state saved as isLoggedIn 

// Sample login Page
const LoginPage = () => {
  const history = useHistory();
  let location = useLocation();
  const { isLoggedIn, login } = useContext(AuthContext);

  const { from } = location.state || { from: { pathname: "/" } };
  const { pathname } = from;

  let handleSubmit= userId => {
    // login is to the fake Api, but yours could be to an axios server.
    login({ userId, history, from });
  };

  return isLoggedIn ? (
    "you are already logged in"
  ) : (
    <div className="login-btns">
      {pathname !== "/" && (
        <p>You must log in to view the page at {pathname}</p>
      )}
      <button onClick={() => handleSubmit("player-1")}>Player Logs in</button>
      <button onClick={() => handleSubmit("admin-1")}>Admin Logs in</button>
    </div>
  );
};

With your data readily accessible in all components via context, you can use it to translate the privilages into conditions to render the components. Tip Name conditions related to the views you are rendering not the api, since it changes a lot. You could retrieve the privilages from the context in whichever descendant component you wish to conditionally render as follows

const { currentUser, isLoggedIn } = useContext(AuthContext);
const privilege = currentUser?.privilege || [];

// Create View conditions based on the privilages. You can be fancy all you want :)
const canViewProfile = privilege === "PLAYER" || privilege === "ADMIN";
const canRegisterUser = privilege === "ADMIN";

You could use this logic directly in your Navigation component but chances are high, some Routes and Switch will depend on this logic for conditional redirection. So it's best to avoid repetition, to keep it in the siblings' parent, or even compute it in the context/store. (Tip: Trying to maintain the same related condition in many different places bites especially without TypeScript).

In my case, I pass the conditions to the Navigation and Pages via props. See the AuthedComponents ==== to your App component below


// This is similar to your App component
const AuthedComponents = () => {
  const { currentUser, isLoggedIn } = useContext(AuthContext);
  const privilege = currentUser?.privilege || [];

  // Generate conditions here from the privilages. You could store them in the context too
  const canViewProfile = privilege === "PLAYER" || privilege === "ADMIN";
  const canRegisterUser = privilege === "ADMIN";

  return (
    <Router>
      <div>
        <h1>{` ⚽ Soccerway `}</h1>
        <UserProfile />

       {/* Pass the conditions to the Navigation. */}
        <Navigation
          isLoggedIn={isLoggedIn}
          canViewProfile={canViewProfile}
          canRegisterUser={canRegisterUser}
        />

        <hr />

        <Switch>
          <Route path="/login">
            <LoginPage />
          </Route>
          <Route path="/about-us">
            <AboutUsPage />
          </Route>

          {/* You can conditionally render hide these items from the tree using permissions */}
          <Route path="/profile">
            {/* Passed down the conditions to the Pages via props to be used in redirection */}
            <ProfilePage canViewProfile={canViewProfile} />
          </Route>
          <Route path="/register-user">
            <RegistrationPage canRegisterUser={canRegisterUser} />
          </Route>

          <Route path="/">
            <HomePage />
          </Route>
        </Switch>
      </div>
    </Router>
  );
};

In the Navigation component, use isLoggedIn prop to either show the login NavLink item or the (Profile and Registration pages) since they are mutually exclusive. Conditionally render privilege based NavLinks with the computed props.

/* You could get these props from the auth context too... if you want */
const Navigation = ({ isLoggedIn, canViewProfile, canRegisterUser }) => (
  <ul className="navbar">
    <li>
      <NavLink exact to="/" activeClassName="active-link">
        Home
      </NavLink>
    </li>
    {/* Check if the User is Logged in: Show the Login Button or Show Other Nav Buttons */}
    {!isLoggedIn ? (
      <li>
        <NavLink to="/login" activeClassName="active-link">
          Login
        </NavLink>
      </li>
    ) : (
      // Now, here consitionally check for each permission.
      // Or you could group the different persmissions into a user-case
      // You could have this as s seperate navbar for complicated use-cases
      <>
        {canViewProfile && (
          <li>
            <NavLink to="/profile" activeClassName="active-link">
              Profile
            </NavLink>
          </li>
        )}
        {canRegisterUser && (
          <li>
            <NavLink to="/register-user" activeClassName="active-link">
              Register
            </NavLink>
          </li>
        )}
      </>
    )}
    {/* This is a public route like the Home, its viewable to every one  */}
    <li>
      <NavLink to="/about-us" activeClassName="active-link">
        AboutUs
      </NavLink>
    </li>
  </ul>
);

In the components, if the user does not meet the permissions/privileges, forcefully redirect them to the login page.


// Example usage in the Profile Page
const ProfilePage = ({ canViewProfile }) => {
  return canViewProfile ? (
    <>
      <h2>Profile</h2>
      <p>Full details about the Player</p>
    </>
  ) : (
    <Redirect from="/profile" to="/login" />
  );
};



来源:https://stackoverflow.com/questions/62038146/once-login-is-successful-how-to-navigate-and-display-nav-links

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!