Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Render react component based on user permissions

I need to render a component based on the user role.

For example: If the user is a sales persons he would see both buttons. But if he is support he would see only the second button.

import React from 'react';
import Button from './MyCustomButton';

class App extends React.PureComponent {

  render() {
    return (
      <Grid>
        <Button> // visible only for sales manager
          Action A
        </Button>
        <Button> // visible for support and sales manager
          Action B
        </Button>
      </Grid>
    )
  }
}

I would really like to avoid if-else statements inside the render. Keep it clean as possible.

Is there a good design or some tools/decorator that could be useful here?

I've already handled it in the server side but I need a clean solution for the client side as well.

thanks.

like image 992
Idan Dagan Avatar asked Nov 02 '18 06:11

Idan Dagan


Video Answer


3 Answers

You can inline ternary statements directly into your component tree, as shown below. If these boolean expression would evaluate to false, react will skip them automatically.

class App extends React.PureComponent {

  render() {
    const user = this.props.user;
    return (
      <Grid>
        {user.isSalesManager && <Button>
          Action A
        </Button>}
        {(user.isSalesManager || user.isSupport) && <Button>
          Action B
        </Button>}
      </Grid>
    )
  }
}

In order to do that, you need to have the information about the user role in the props though.

Read more on conditional rendering in the React documentation.

like image 106
jotaen Avatar answered Oct 25 '22 18:10

jotaen


One way you could do this is with an Authorizor component. It would be a very simple function that does your validation for you like normal, and just checks an authLevel. If you need to adjust this to work with your auth model thats fine.

function Authorizor(props) {
  if (props.authLevel > props.user.authLevel) {
    return null;
  }
  const { ComponentToValidate } = props;
  return <ComponentToValidate {...props} />;
}

then usage would be something like

<Authorizor authLevel={1} ComponentToValidate={Button} {...this.props} />

in your example this would look like.

class App extends React.PureComponent {

  render() {
    return (
      <Grid>
        <Authorizor authLevel={2} ComponentToValidate={Button} label="Action A" onClick={this.handleActionAThingy} {...this.props} />
        <Authorizor authLevel={1} ComponentToValidate={Button} label="Action B" onClick={this.handleActionBThingy} {...this.props} />
      </Grid>
    )
  }
}
like image 39
John Ruddell Avatar answered Oct 25 '22 16:10

John Ruddell


If it's just a matter of 'cleaner' look and not lack of condition (because you will have to have it somewhere) you could create simple reusable component:

const Visibility = ({ isVisible, children }) => (
  isVisible ? React.Children.only(children) : null;
)

and use it like that

      <Grid>
        <Visibility isVisible={user.isSalesManager}>
          <Button> // visible only for sales manager
            Action A
          </Button>
        </Visibility>
        <Visibility isVisible={user.isSalesManager || user.isSupport}>
          <Button> // visible for support and sales manager
            Action B
          </Button>
        </Visibility>
      </Grid>

it's hard to come up with much better solution without knowing more about your application architecture, but if you keep your user roles in some kind of store accessible from anywhere (redux, context, whatever) you could create very similar component that accepts roles needed to see it's children (let's say an array of strings) and possibly some kind of descriptor (like all, oneOf etc. if your permissions system is more complicated) and component itself grabs needed data from global store and compares it to props it got. If that's your use case i can provide example.

like image 1
simka Avatar answered Oct 25 '22 18:10

simka