Codementor Events

Using Higher Order Components for Authenticated Routing

Published Mar 28, 2018

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>
);
Discover and read more posts from Sahil Mittal
get started
post comments4Replies
glitzsiva
3 years ago

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

sheikhzubayr
5 years ago

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?

Lola Swift
5 years ago

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:

 describe("<Auth />", () => {
     beforeEach(() => {
      const mockStore = configureStore();

// creates the store with any initial state or middleware needed
store = mockStore({
 isAuthenticated: false
  });
});
it("Should render the component only when auth prop is true", () => {
   const Component = <h1>Hola</h1>;
   //const ConditionalHOC = Auth(Component);
   const wrapper = shallow(
  <Auth store={store}>
    <h1 className="hello">Hello</h1>
  </Auth>
);
expect(wrapper).not.toBe(null);
  });
  });
Show more replies