Well, I think I understand what you want. I have done something that works for me, and I like the way I have it, but I understand that there are other viable solutions.
What I wrote was a HOC-style jet router.
Basically, I have PermissionsProvider where I run user permissions. I have another withPermissions HOC that introduces the permissions granted earlier to my component.
Therefore, if I ever need to check permissions on this particular component, I can easily access them.
// PermissionsProvider.js import React, { Component } from "react"; import PropTypes from "prop-types"; import hoistStatics from "hoist-non-react-statics"; class PermissionsProvider extends React.Component { static propTypes = { permissions: PropTypes.array.isRequired, }; static contextTypes = { permissions: PropTypes.array, }; static childContextTypes = { permissions: PropTypes.array.isRequired, }; getChildContext() { // maybe you want to transform the permissions somehow // maybe run them through some helpers. situational stuff // otherwise just return the object with the props.permissions // const permissions = doSomething(this.props.permissions); // maybe add some validation methods return { permissions: this.props.permissions }; } render() { return React.Children.only(this.props.children); } } const withPermissions = Component => { const C = (props, context) => { const { wrappedComponentRef, ...remainingProps } = props; return ( <Component permissions={context.permissions} {...remainingProps} ref={wrappedComponentRef} /> ); }; C.displayName = `withPermissions(${Component.displayName || Component.name})`; C.WrappedComponent = Component; C.propTypes = { wrappedComponentRef: PropTypes.func }; C.contextTypes = { permissions: PropTypes.array.isRequired }; return hoistStatics(C, Component); }; export { PermissionsProvider as default, withPermissions };
Ok, I know this is a lot of code. But this is HOC (you can find out more here ).
Higher Order Component (HOC) is the best practice in React for reusing component logic. HOCs are not part of the React API as such. They are a sample that arises from a representative composition. Specifically, a higher-order component is a function that takes a component and returns a new component.
Basically, I did this because I was inspired by the reaction of the router. Whenever you want to know some routing materials, you can simply add the @withRouter decorator and they will add the details to your component. So why not do the same?
- You must first set permissions on the provider
- Then you use the withPermissions decorator on components that check permissions
//App render return ( <PermissionsProvider permissions={permissions}> <SomeStuff /> </PermissionsProvider> );
Somewhere inside SomeStuff , do you have a widespread toolbar that checks permissions?
@withPermissions export default class Toolbar extends React.Component { render() { const { permissions } = this.props; return permissions.canDoStuff ? <RenderStuff /> : <HeCantDoStuff />; } }
If you cannot use decorators, you export a toolbar, for example,
export default withPermissions(Toolbar);
Here is the code I showed in practice:
https://codesandbox.io/s/lxor8v3pkz
NOTES:
- I really simplified the permissions because this logic comes from your goal and for demonstration purposes I simplified them.
- I assumed that the permissions were an array, so I check PropTypes.array in HOC
- This is a very long and complicated answer, and I tried to formulate my best abilities. Please do not measure me for some errors here and there :)