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?
onEnter is great, and useful in certain situations. However, here are some common authentication and authorization problems onEnter does not solve:
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>);
}
}
This seems a very interesting possibility. I hit this question Googling the same issue, and this is a new library so I figured doesn't hurt to link it in case anyone else can be helped by it. I have not decided if I am going to go this route myself yet, as I am 15 minutes into the Google-palooza. It's called CASL.
Link to the Article Explaining the Library
Link to the Library
Example Code from the Library per request:
if (ability.can('delete', post)) {
<button onClick={this.deletePost.bind(this}>Delete</button>
}
replaces something like:
if (user.role === ADMIN || user.auth && post.author === user.id) {
<button onClick={this.deletePost.bind(this}>Delete</button>
}
Which in the article the author furthered with a custom component to get:
<Can run="delete" on={this.props.todo}>
<button onClick={this.deleteTodo.bind(this}>Delete</button>
</Can>
It basically allows the developer to be more declarative in their code for ease of use and maintainability.
If you use react-router
, the recommended way to handle authorization is through the onEnter
property in the Route
component.
<Route path="/" component={Component} onEnter={Component.onEnter} />
See the docs.
And it's also an answer to your question:
Is there a way to access the properties/methods of an exported React component such that they can be accessed from a containing component?
So just make them static properties/methods (like Component.onEnter).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With