React patterns: Privileged Content

In the last project I worked on, some parts in the page should only be shown to privileged users. As easy as it would be to just add that bit of logic in a render method, it would not be idiomatic React, and it would also get cumbersome when the validation logic becomes more complex, besides the fact that we would have to add similar logic in many places in the code. DRY, as they say.

React makes it easy to solve that situation by making a component that can wrap any part of the app to hide it from unprivileged eyes.

This is the simple solution I’ve been using for that purpose instead:

import React from "react";

class PrivilegedContent extends React.PureComponent {
  state = {
      admin: isAdmin()
  };

  render() {
    if (this.state.admin !== true) {
      return <div />;
    }

    const { children, ...rest } = this.props;

    React.Children.only(children);
    const childrenWithProps = React.Children.map(children, child =>
      React.cloneElement(child, rest)
    );

    return {childrenWithProps};
  }
}

When this component mounts, it sets state.admin to the value of the isAdmin function. If state.admin is false, render will only render an empty <div /> element. Otherwise it will render the children of PrivilegedContent.

At this point, we could simply do return children and hope for the best. It may even seem to work in simple cases, but it will explode in our faces if we use React-based UI frameworks like Material-UI. That’s because these frameworks may be passing specific props to nested components in order to render them properly, and putting an PrivilegedContent in between these nested components will cut the flow of props being passed down.

In order to render the children properly, we have to make sure that any props passed to PrivilegedContent also get passed to the component it wraps. For this, we first verify that our component only has one child by using React.Children.only, which “Verifies that children has only one child (a React element) and returns it. Otherwise this method throws an error”. Then we clone our children and append the props passed to PrivilegedContent to the first (and only child) using React.Children.map.

At this point, childrenWithProps is a clone of our children structure with added props that were passed to our PrivilegedContent.

You can use it like this:

Potential improvements

There are several improvements that can be done to PrivilegedContent. Here are some ideas:

  • We could pass the validation function (isAdmin in the code above) as a prop to make the component more flexible. In case there is no validation prop, it can use one by default.

  • The validation function should probably be asynchronous. In our example we use a synchronous isAdmin function, but if we need to contact a server for validation, for example, our code above won’t work.

  • The user could pass an extra placeholderContent prop that the component uses when it doesn’t pass validation, instead of using a generic div tag like it does now.

Can you think of more improvements? Let me know in the comments!