Using Higher Order Components for Authenticated Routing
A Higher Order Component (HOC) is just a React Component that wraps another one.
The react-redux connect(mapStateToProps, mapDispatchToProps)(Component)
pattern is an example of using the connect()
HOC to extend a React component such that its this.props
is actually connected to values and actions in the Redux store.
Let's say we have some pages that we only want authenticated users to view it (a very common use case); we can use HOC here to handle this!
Let's define our requiresAuth
HOC; assuming we are using the Redux store to store the auth status of the user:
./components/requiresAuth.js
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
export default function (ComposedComponent) {
class Authenticate extends React.Component {
static propTypes = {
isAuthenticated: PropTypes.boolean,
redirect: PropTypes.func.isRequired
};
componentDidMount() {
this._checkAndRedirect();
}
componentDidUpdate() {
this._checkAndRedirect();
}
_checkAndRedirect() {
const { isAuthenticated, redirect } = this.props;
if (!isAuthenticated) {
redirect();
}
}
render() {
return (
<div>
{ this.props.isAuthenticated ? <ComposedComponent {...this.props} /> : null }
</div>
);
}
}
const mapStateToProps = (state) => {
return {
isAuthenticated: state.auth.isAuthenticated
};
};
const mapDispatchToProps = dispatch => bindActionCreators({
redirect: () => push('/signin')
}, dispatch)
Authenticate.propTypes = propTypes
return connect(
mapStateToProps,
mapDispatchToProps
)(Authenticate);
}
So, requiresAuth
is a function that takes ComposedComponent
and returns AuthenticatedComponent
. AuthenticatedComponent
wraps the original component, plus it checks if the user already authenticated, in case he is not, it will redirect to /signup
. Pretty straightforward eh?
Now we can use requiresAuth HOC and update our component where we have defined the routes:
./components/route.js
...
import requireAuth from './requiresAuth';
...
const routes = (
<Route path="">
<Route path="/signin" component={SignIn} />
...
<Route path="/app" component={requiresAuth(App)}>
<Route name="dashboard" path="dashboard" component={Dashboard} />
<Route name="profile" path="user-profile" component={UserProfile} />
</Route>
...
</Route>
);
thanks for for your write up
And I have doubt why you have used mapDispatchToProps() function instead of we can directly push _checkAndRedirect() as (this.props.history.push(’/signin’)) if there is any advantage of using redirect if its can please explain or share git hub link
Thanking You,
Glitz Siva
I tried it out and could get it up and running. However I want to now disabled/hide the navlink if the route doesn’t render the component(if user is not authenticated).
How to disable or hide NavLink, if routing is not rendered?
Hi Sahil, many thx for the write up. How to test this component using redux-mock-store, jest and enzyme then? I tried something like this but it always passed: