TL; DR: Условно отобразите NavLinks
в компоненте Navigation
. Оформить заказ в песочнице .
НЕКОТОРЫЙ контекст.
@ soccerway, поскольку на этот вопрос можно ответить с помощью того же подхода в ранее ответил на один , для краткости я переработал эту песочницу кода с небольшими изменениями, чтобы попытаться воспроизвести ваш случай со следующими предположениями ...
Похоже, вы используете локальное состояние с useState
при входе в систему на основе этого оператора setLoginData(res.data.loginData)
, но поскольку ваш компонент может быть отключен от панели навигации, учитывая тот факт, что у вас нет другой панели навигации или панели инструментов и ваши пользователи обязательно будут легко перемещаться вперед и назад, размонтирование этого компонента приведет к тому, что приложение потеряет это состояние. Намного лучше использовать более высокое управление состоянием, сохраняя данные Auth и Privilege между страницами. Вы можете использовать React's Context и получить к нему доступ с помощью useContext hook
или использовать Redux и обернуть данные сеанса вокруг всего приложения. После входа пользователя в систему сохраните состояние приложения в контексте или в магазине и извлеките его в любом компоненте, который должен иметь это разрешение / условие привилегии. В моем случае я использую api api контекста и сохраняю идентификатор пользователя в localStorage . (Вы можете использовать любое хранилище сеансов, которое захотите.)
Поскольку у меня нет доступа к вашему api, я создал простой fake Auth API
и для покрытия handleSubmit
. В AuthProvider
я предполагал, что данные, которые вы получаете с сервера в этой строке res.data.loginData[0].privilege === "PLAYER"
, имеют следующий формат, но это может быть что угодно.
// 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, поскольку они сильно меняются . Вы можете получить привилегии из контекста в зависимости от того, какой компонент-потомок вы будете sh для условного рендеринга следующим образом:
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";
Вы можете использовать этот logi c непосредственно в своем Navigation
компоненте, но шансы высоки , некоторые маршруты и коммутаторы будут зависеть от этого logi c для условного перенаправления. Так что лучше избегать повторений, хранить их в родителях братьев и сестер или даже вычислять их в контексте / хранилище. (Совет: Попытка поддерживать одно и то же связанное состояние во многих разных местах укусит, особенно без TypeScript ).
В моем случае я передаю условия в Navigation
и Pages
через реквизит. См. AuthedComponents ==== to your App component
ниже
// 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
prop, чтобы отобразить элемент входа NavLink
или (страницы профиля и регистрации), поскольку они являются взаимоисключающими. Условно отображать ссылки NavLink на основе привилегий с вычисленными реквизитами.
/* 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>
);
В компонентах, если пользователь не соответствует разрешениям / привилегиям, принудительно перенаправить их на страницу входа.
// 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" />
);
};