Hide Element Dynamically in ReactJS

Viewed 429

I have concern about managing access right in web side. A user with Admin Role can see everything in web. A user with Finance Role just can see some of elements. I manage this condition in database like these.

  • Admin can access: everything
  • Finance can access elements: A, B
  • Warehouse can access elements: A, C
<div id="a">
  Component A
</div>
<div id="b">
  Component B
</div>
<div id="c">
  Component C
</div>

I can do manually by doing this:

export default props => {
  const allowedElements = ['a', 'b']; // get by role of current user
  return (
    <>
      {allowedElements.indexOf('a') >= 0 && <div>Component A</div>}
      {allowedElements.indexOf('b') >= 0 && <div>Component B</div>}
      {allowedElements.indexOf('c') >= 0 && <div>Component C</div>}
    </>
  );
}

I hope there is effective / simple ways to handle this condition. Can you give suggestion to me?

Additional The solution I hope can handle nested element. In below code, element B and C one of them can hide based on role.

<div id="a">
  Component A
</div>
<div>
  <div id="b">
    Component B
  </div>
  <div id="c">
    Component C
  </div>
</div>

3 Answers

For example using some object map:

const rolesMap = {
 a: <div>Component A</div>,
 b: <div>Component B</div>,
 c: <div>Component C</div>,
}

render:

const roles = ['a', 'b'];

return roles.map(role => rolesMap[role])

JSFiddle: https://jsfiddle.net/7qa4jxbz/

(Object) Map is always better than some if/loops.

Be inform that you are using client javascript - everyone can access your source code, you should handle admins operations on API.

You can create a custom component that wraps the component you want to show/hide based on the user role.

const RoleAccessibleComponent = ({ permittedRoles, children }) => {
  // Access current user object from here and check if user role is included in the permitted roles
  // permittedRoles could be an array so you can allow multiple roles access same component
  // const userRole = getCurrentUserRole();
  const hasAccess = permittedRoles.includes(userRole);
  if (hasAccess) {
    return children
  }

  return null;
}

And in your render block, you might do.

<RoleAccessibleComponent permittedRoles={['admin', 'finance', 'warehouse']}>
  <div id="a">
    // Component A
  </div>
</RoleAccessibleComponent>

<div>
  <RoleAccessibleComponent permittedRoles={['admin', 'finance']}>
    <div id="b">
      // Component B
    </div>
  </RoleAccessibleComponent>
  <RoleAccessibleComponent permittedRoles={['admin', 'warehouse']}>
    <div id="c">
      // Component C
    </div>
  </RoleAccessibleComponent>
</div>

This is a base concept with which you can easily add and remove the roles that can view a component. Can be further modified or abstracted based on your business needs/requirements.

Extras:

  1. You can take it up a notch by passing the component you want to render to the RoleAccessibleComponent like so
const RoleAccessibleComponent = ({ permittedRoles, component: Component }) => {
  const hasAccess = permittedRoles.includes(userRole);
  if (hasAccess) {
    return <Component />
  }
  return null;
}

// Render
<RoleAccessibleComponent
  permittedRoles={['admin', 'finance', 'warehouse']}
  component={ComponentA}
/>
  1. Or use a render prop
const RoleAccessibleComponent = ({ permittedRoles, render }) => {
const hasAccess = permittedRoles.includes(userRole);
  if (hasAccess) {
    return render()
  }
  return null;
}

// Render
<RoleAccessibleComponent
  permittedRoles={['admin', 'finance', 'warehouse']}
  render={() => <ComponentA />}
/>

After few times to googling, I get React.Children. By using this, I can modify 'everything'.

  • Loop through our elements and it's children using React.Children.map
  • On every elements and child, check if ID is authorize to access, show it
  • element who have not ID just return as is.

Code Sandbox: https://codesandbox.io/s/heuristic-wilbur-kk3r1

Before element really returned, I will evaluate it's show/or hide

  const b = React.Children.map(myComponent, (child, i) => {
    return check(child);
  });

It's logic to iterate through elements and child

  const check = child => {
    const p = child.props;
    if (p.id && allowedElements.indexOf(p.id) < 0) return null;
    else if (Array.isArray(p.children)) return p.children.map(c => check(c));
    else if (typeof p.children === "object") return check(p.children);
    else return child;
  };
Related