且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

登录成功后,如何导航和显示导航链接

更新时间:2023-08-25 14:19:28

TLDR:有条件地在Navigation组件中渲染NavLinks. 检出沙箱.

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

某些上下文.

@soccerway,因为可以使用相同的方法在中进行回答一个,为简便起见,我回收了

@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. 基于此语句setLoginData(res.data.loginData),您似乎在登录时将本地状态与useState一起使用,但是由于导航栏可能会卸载您的组件,原因是您没有其他导航栏或仪表板,您的用户注定要轻松地来回移动,卸载该组件将导致应用失去该状态.***使用更高级别的状态管理,以在页面之间保留Auth和Privilege数据.您可以使用 React的上下文并使用 Redux ,并将会话数据包装在整个应用中.用户登录后,将应用程序状态保存在上下文或商店中,并在需要具有许可/特权条件的任何组件中进行检索.在我的情况下,我使用上下文api api,并将用户ID存储在 localStorage中.(您可以使用所需的任何会话存储.)

  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.)

由于我无权访问您的api,因此我创建了一个简单的行中从服务器获取的数据具有以下格式,但是可以是任何格式.

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>
  );
};


通过上下文可以轻松访问所有组件中的数据,您可以使用它将特权转换为呈现组件的条件. Tip 与要渲染的视图相关的名称条件,而不是api,因为它发生了很大的变化.您可以从上下文中检索希望按条件呈现的后代组件中的特权,如下所示:


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";

您可以直接在Navigation组件中使用此逻辑,但是机会很高,某些路由和交换机将依赖此逻辑进行条件重定向.因此,***避免重复,将其保留在兄弟姐妹的父母中,甚至在上下文/存储中进行计算. (提示:试图在许多不同的地方保持相同的相关条件,尤其是在没有TypeScript的情况下).

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).

在我的情况下,我通过道具将条件传递给NavigationPages.参见下面的AuthedComponents ==== to your App component

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>
  );
};

在导航组件中,使用isLoggedIn属性显示登录NavLink项或(配置文件和注册页面),因为它们是互斥的.使用计算的道具有条件地呈现基于特权的NavLink.

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" />
  );
};