I'm struggling to implement dynamic role-based routing with the latest version of react-router-dom (v6.22.3). I have some ideas, but I'm finding it difficult to implement them fully.
Notes:
- I familiar with examples described here [link][1]
- Goal description :
- define roles in the router
- consume it in the outlet
- The entire path are protected except login. so we dont need to add protected route (the app is function as protected) , just need to handle the role base
Here's what I've tried:
I've set up a mapping for my pages and defined roles for each page:
export const paths = {
boo: { path: "/boo", roles: [] },
foo: { path: "/foo", roles: ["admin", "manager"] },
};
And here's how I've set up my router: (defining the roles inside the id prop is the best option i found)
const router = createBrowserRouter([
{
path: "/",
element: <App />,
children: [
{ path: paths.details.boo, element: <Boo/>, id: createRouteId("boo") },
{ path: paths.details.foo, element: <Foo/>, id: createRouteId("Foo") },
],
errorElement: <ErrorPage />,
},
{
path: "/login",
element: <Login />,
},
]);
export default router;
Now, I'm wondering if there's any way to use a property other than "id" to uniquely identify routes. However, let's assume "id" is the best option.
In my App.tsx, I'm using a custom hook to get the user object, which looks like this:
{userName:string,roles:roles[],...}
I'm also attempting to check if the user has valid roles for the current route:
const isValidRole = (userRoles: string[], outletRole: string[]) => {
return userRoles.some((role) => outletRole.includes(role));
};
Here's my App.tsx component: (here we use outlet and i try to consume the roles based on the location with the outlet)
function App() {
const { user } = useAuth();
const getRouteIdFromOutlet = useOutlet();
const [isValidRole] = useState(isRoleValid(user.roles, JSON.parse(getRouteIdFromOutlet.props.id)));
if (!user) return <Login />;
if(!isValidRole) throw new Error("Unauthorized"); // the error page will know what to render
return (
<Layout>
<NavbarDesktop />
<Content style={{ padding: "0 48px" }}>
<Outlet />
</Content>
</Layout>
);
}
now the const getRouteIdFromOutlet = useOutlet(); is the closest option i found in order to read the id from the route and utilize it here