根据用户角色在react-router中有条件地渲染路由的最佳方法是什么。我遇到的情况是,并非所有角色都有权查看某些路线。我还需要处理子路线。因此,如果主要路线之一类似于 /posts,我只希望管理员和学生访问该路线及其子路线(例如 posts/today)。我正在使用react-router版本5.3.2。
我对react-router-dom v6也有类似的问题,但你可以调整它以适应你的好处
首先创建一个事实来源,无论用户是否可以访问某些路由,对于基于角色的身份验证,我会考虑像这样的钩子
export function useUserRoles() {
// some logic or api call to get the roles
// for demonstration purposes it's just hard coded
const userRoles: Array<typeof UserRoles[number]> = ['admin', 'root'];
// return the current user roles
return userRoles;
}
然后我们创建一个组件,可以决定使用该钩子是渲染路由还是重定向到登录(或某个页面)
export function RolesAuthRoute({ children, roles }: { children: ReactNode, roles: Array<typeof UserRoles[number]> }) {
const userRoles = useUserRoles();
const canAccess = userRoles.some(userRole => roles.includes(userRole));
if (canAccess)
return (
<Fragment>
{children}
</Fragment>
);
return (<Navigate to="/dashboard/login" />);
}
然后定义的路由包装该组件以充当警卫,决定您是否可以访问该路由
<Route
path="users"
element={
<Suspense fallback={<ProjectLayoutLoader />}>
<RolesAuthRoute roles={['admin']}>
<ProjectUsersPage />
</RolesAuthRoute>
</Suspense>
} />
我有两个角色“家长”和“经理”,我编写这段代码并为我工作:
function App(props) {
return (
<div className="App ">
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/" element={<ProtectedRoute />}>
<Route exact path="/admin" element={<StudentLists />} />
<Route exact path="/student_pk_api/:id" element={<IdFilter />} />
<Route exact path="/absentes" element={<AbsentStu />} />
<Route exact path="/students_by_field_and_level_:grade_api/:studyfield_code" element={<FieldGradeFilter />} />
<Route exact path="/absent_yesterday_api" element={<YesterdayAbs />} />
<Route exact path="/absent_today_api" element={<TodayAbs />} />
</Route>
<Route exact path="/" element={<ProtectedUserRoute />}>
<Route exact path="/users" element={<SchoolDit />} />
</Route>
</Routes>
</div>
);
}
为了检查角色,我编写了两个函数:
对于经理角色:
const ProtectedRoute = () => {
const [manager, setmaneger] = useState(localStorage.getItem("role")==="manager"?true:null);
return manager ? <Outlet /> : <Navigate to="/" />;
}
export default ProtectedRoute
对于家长角色:
const ProtectedUserRoute = () => {
const [parent, setParent] = useState(localStorage.getItem("role")==="parent"?true:null);
return parent ? <Outlet /> : <Navigate to="/" />;
}
export default ProtectedUserRoute
当然,有很多方法可以实现这一点。我将与 React Router DOM v6 分享我的工作示例。
步骤:
useOutlet
检索路径数据。那么让我们开始吧。
1。 useAuth 钩子
const token = await getToken()
const useAuth = () => {
const [user, setUser] = useState<user | null>(null);
useEffect(() => {
if (token) {
const isTokenValid = checkTokenValidity(token);
const userData = isTokenValid ? decodeToken(token) : null;
if (isTokenValid) {
setUser(userData);
} else {
setUser(null);
removeCookie("token");
}
} else {
setUser(null);
}
}, []);
return { user };
};
** 我认为内部方法是直接的,不需要扩展
现在让我们定义路由器并创建角色映射对象。
2。定义路径角色映射对象
export const paths = {
boo: { label: "Boo", path: "boo", roles: null },
foo: { label: "Foo", path: "foo", roles: ["admin"] },
zoo: { label: "API", path: "api", roles: ["admin", "manager"] },
};
3.定义路由器并使用路径映射对象
const createRouteId = (key: keyof typeof paths) => {
return JSON.stringify({ [key]: paths[key].roles });
};
export const router = createBrowserRouter([
{
path: "/",
element: <ProtectedRoute />,
children: [
{ path: paths.boo.path, element: <Boo/>, id: createRouteId("boo") },
{ path: paths.foo.path, element: <Foo />, id: createRouteId("foo") },
{ path: paths.zoo.path, element: <Zoo/>, id: createRouteId("zoo") }
],
errorElement: <ErrorPage />,
}]
id
属性应该是唯一的,并使用方法createRouteId
包含每个路径的角色数据
const isRoleValid = (userRoles: string[] | undefined, outletRole: string[] | null) => {
if (!userRoles) return false;
if (!outletRole) return true;
return userRoles.some((role) => outletRole.includes(role));
};
function ProtectdRoute() {
const { user } = useAuth();
const location = useLocation();
const getRouteIdFromOutlet = useOutlet();
useEffect(() => {
const routeLocationData = getRouteIdFromOutlet?.props.children.props.match.route;
const locationKey = routeLocationData?.path;
const locationRolesObj = routeLocationData?.id && JSON.parse(routeLocationData?.id);
if (locationRolesObj && locationKey) {
const locationRoles = locationRolesObj[locationKey];
if (!isRoleValid(user?.roles, locationRoles)) {
throw new Response("unauthorized", { status: 403 });
}
}
}, [user, location.pathname]);
return user ? <Outlet /> : <Login />
}
useOutlt 非常嵌套,但它确实包含 id 信息,其中包括每个特定路径的角色。