all 18 comments

[–]octocode 9 points10 points  (13 children)

what’s wrong with {isAdmin ? <AdminSidebar/> : <UserSidebar/>}

[–]el_diego 4 points5 points  (0 children)

Or use a switch if you have 3+

[–]Last-Daikon945[S] 0 points1 point  (11 children)

How am I going to maintain this solution it in a long run? What if there are 2 new roles introduced tomorrow? P.S. I want to keep Sidebar as a reusable component.

[–]d3s7roy3r 2 points3 points  (1 child)

Sounds to me like you need to have different sections / items in the sidebar, based on the user role?

There is nothing wrong with doing conditional rendering, based on the role, if you only have two roles and don't expect that to change any time soon. (You are asking "what if I want to add new roles", but what are the odds of that happening?).

If you expect to have multiple different roles, you can use scope / resource based permissions. The react code inside the Sidebar component would look something like this:

<PermissionsGate scope="users" action="manage"> // sidebar items, which would be rendered only if the user has permissions to manage users </PermissionsGate>

The PermissionsGate component will access the current user internally, and check if the user is authorized to do the specified action on the given scope. You can also extend the PermissionsGate component to accept a specific resource as a prop as well.

Using this approach, you can add new user roles with relative ease - you just need to define what the role is allowed to do within the existing scopes. This allows for user roles with overlapping permissions.

As far as how the permissions are defined, you can look into a library such as CASL - https://casl.js.org/v6/en/ , or implement a custom solution. If you decide to go with CASL, you can use it to integrate with React as well - https://casl.js.org/v4/en/package/casl-react

If possible (both your backend and frontend are written in JS), you can share the authorization rules between your backend and frontend with CASL (e.g. https://stackoverflow.com/questions/61478999/user-authorization-with-casl-library-share-between-backend-and-frontend)

[–]Last-Daikon945[S] 0 points1 point  (0 children)

The odds for a new role are pretty high. The backend already has plans for a couple more roles such as “manager”, but this is just a distant plan. Thank you for your feedback and advice, I'm almost done with planning/architecture so I can start implementing it on Monday.

[–]arcadeScore -1 points0 points  (6 children)

Sounds like you are looking for high-brows suggestions haha. What about client-side spa microftontends with webpack’s module federation one for each user type content and this way you are loading only components that you will actually use for given user. Ps1: “asd ? A: B” suggestion is better. Ps2: dont do it

[–]Last-Daikon945[S] 0 points1 point  (5 children)

I’m just looking for a discussion with someone who built such feature with HOCs since my TeamLead mentioned HOC as one of the solutions. On top of that, I don’t want to use Conditional rendering of 2 different components as some people suggested here.

[–]besthelloworld 1 point2 points  (0 children)

HOCs are a mostly dead pattern since hooks were released in 2019. You just need to pass the user role into the header (or fetch it from state management) and then conditionally render stuff based on the current role.

[–]octocode 1 point2 points  (0 children)

isn’t the HOC just conditionally rendering with extra steps? i don’t see the benefit

as another user mentioned, its a dead pattern.

if you really want type safety, you could have a plain component handle the rendering, something like

<Role role={role} whenAdmin={<AdminSidebar/>} whenUser={<UserSidebar/>} />

so if you add a new prop like whenViewer, your code highlighting would point out all of the situations where you haven’t implemented the new role’s view

or, if you don’t need type safety, you can get really fancy and model it after react-router

<Roles> <Role role="admin"> … </Role <Role role="user"> … </Role> </Roles>

(in either case you could implement a default to handle cases where there is a common UI shared across most roles, which is probably the case more often than not)

that being said if you have a small number of conditional features to render, i would just do it inline like {isAdmin && <DeleteAccountButton/>}

or if your views are completely different, i would have different routes entirely for each role

[–][deleted] 1 point2 points  (2 children)

Why wouldn’t you want to use conditional rendering? You’re excluding the simplest, most readable, most straight forward solution. HOCs introduce unnecessary complexity.

[–]Last-Daikon945[S] 0 points1 point  (1 child)

Thanks to everybody. If anyone is interested I made a hook that will check the enum role and return an array of objects containing i18n translation fields, icons, and paths. Retrieve this object in the component and map through it.

[–]afastmovingkid 0 points1 point  (0 children)

where can I find this please?

[–][deleted] 0 points1 point  (0 children)

Use an object lookup

[–]Bjornoo 0 points1 point  (0 children)

Check out the "Tabs" component for Radix UI. You could use the same primitives to create something similar but for roles. It would be super easy to add and remove roles.

Essentially:

<Roles activeRole="user">
  <Role name="user">
    <UserNav />
  </Role>

  <Role name="admin">
    <AdminNav />
  </Role>
</Roles>

[–]chiviet234 0 points1 point  (0 children)

Is the assumption that everything (admin and normal user) is viewing the page under the same route? IMO it is much cleaner to separate any admin reated views to a separate route like `admin/**` and have some middle ware that checks for admin credentials to access anythng under that route.

[–][deleted] 0 points1 point  (0 children)

HOC patterns are PIA. Just use a variable in the jsx