Himmel Himmel - 3 months ago 13
React JSX Question

Permission checks from components in a React/Redux application

I'm trying to work with a team building a React application, and trying to figure out the best way to create a "higher-order" React component (one that wraps another) to perform Authentication in conjunction with the Redux data store.

My approach thus far has been to create a module that consists of a function that returns a new React component depending on whether or not there is an authenticated user.

export default function auth(Component) {

class Authenticated extends React.Component {

// conditional logic

render(){
const isAuth = this.props.isAuthenticated;

return (
<div>
{isAuth ? <Component {...this.props} /> : null}
</div>
)
}
}

...

return connect(mapStateToProps)(Authenticated);

}


This makes it easy for other people on my team to specify whether or not a component requires certain permissions.

render() {
return auth(<MyComponent />);
}


If you are performing role-based checks, this approach makes sense, as you may only have a few roles. In such a case, you could just call
auth(<MyComponent />, admin)
.

Passing arguments becomes unwieldy for permissions-based checks. It may however be feasible to specify permissions at the component level as the components are being constructed (as well as manageable in a team environment). Setting static methods/properties seems like a decent solution, but, as far as I can tell, es6 classes export as functions, which don't reveal callable methods.

Is there a way to access the properties/methods of an exported React component such that they can be accessed from a containing component?

Answer

onEnter is great, and useful in certain situations. However, here are some common authentication and authorization problems onEnter does not solve:

  • Decide authentication/authorization from redux store data (there are some workarounds)
  • Recheck authentication/authorization if the store updates (but not the current route)

  • Recheck authentication/authorization if a child route changes underneath the protected route

An alternative approach is to use Higher Order Components.

You can use Redux-auth-wrapper provides higher-order components for easy to read and apply authentication and authorization constraints for your components.


  • To get child methods you can use:refs, callback and callback from refs

  • To get child props you can use:this.refs.child.props.some or compInstance.props.some

Example for methods and props:

class Parent extends Component {
    constructor(props){
        super(props);
        this.checkChildMethod=this.checkChildMethod.bind(this);
        this.checkChildMethod2=this.checkChildMethod2.bind(this);
        this.checkChildMethod3=this.checkChildMethod3.bind(this);
    }
    checkChildMethod(){
        this.refs.child.someMethod();
        console.log(this.refs.child.props.test);
    }
    checkChildMethod2(){
        this._child2.someMethod();
        console.log(this._child2.props.test);
    }
    checkChildMethod3(){
        this._child3.someMethod();
        console.log(this._child3.props.test);
    }
    render(){
        return (
            <div>
                Parent
                <Child ref="child" test={"prop of child"}/>
                <ChildTwo ref={c=>this._child2=c} test={"prop of child2"}/>
                <ChildThree returnComp={c=>this._child3=c} test={"prop of child3"}/>
                <input type="button" value="Check method of child" onClick={this.checkChildMethod}/>
                <input type="button" value="Check method of childTwo" onClick={this.checkChildMethod2}/>
                <input type="button" value="Check method of childThree" onClick={this.checkChildMethod3}/>
            </div>
        );
    }
}

class Child extends Component {
    someMethod(){
        console.log('someMethod Child');
    }
    render(){
        return (<div>Child</div>);
    }
}
class ChildTwo extends Component {
    someMethod(){
        console.log('someMethod from ChildTwo');
    }
    render(){
        return (<div>Child</div>);
    }
}
class ChildThree extends Component {
    componentDidMount(){
        this.props.returnComp(this);
    }
    someMethod(){
        console.log('someMethod from ChildThree');
    }
    render(){
        return (<div>Child</div>);
    }
}