Codementor Events

Reusing React Component logic with Higher Order Component

Published Nov 23, 2017


HOC

Higher order components are great pattern that has proven to be very valuable . React emphasizes composition over inheritance making it simple to compose larger component by reusing smaller ones.

In this post we will review in detail how to employ Higher Order Component in code reuse and logic abstraction.

What are Higher Order Component ?

Higher order component is a function that takes a component and returns a new component.

This pattern is usually implemented as a function, which is basically a class factory .

React uses higher-order components (HOCs) as an alternative to inheritance. Similar to higher-order functions, which take or return other functions

const EnhancedComponent = higherOrderComponent(wrappedComponent)

if you have used react-redux, you have probably already used at least one higher-order component. One of its primary functions is CONECT which accepts a component and returns a component connected to the Redux store, wrapping the one provided. It saves you the hassle of managing the logic connection to the store in multiple places in your app.

What can I do with HOCs?

HOC can be used for

  • Code reuse and logic abstraction
  • Props manipulation
  • State abstraction and manipulation
  • Render Highjacking

In this article we will focus on code reuse and how we can use Higher-Order Components as an alternative to inheritance

The Problem

let’s say you have an onClick and onChange event handlersaccessing state, and you plan to utilize this event handler in bunch of different components. You could, of course, create a lot of duplicated code. Remember the rule, don’t repeat yourself (DRY).

Let’s say you have a SignIn and a SignUp component

SignIn Component

class SignIn extends React.Component {
     constructor(props) {
         super(props);
         this.state = {
           email: '',
           password: '',
           name: '',
          };
         this.handleInputChange = this.handleInputChange.bind(this);
         this.handleOnSubmit = this.handleOnSubmit.bind(this);
       }
     handleInputChange(event) {
        this.setState({
          [event.target.name]: event.target.value
        });
     }
    handleOnSubmit(event) {
      event.preventDefault();
      const { email, password } = this.state;
      const userObj = {
         email,
         password
       }  
      this.props.signIn(userObj);
    }
   render() {
     const { email, password } = this.state;
     return (
       <form onSubmit={this.handleOnSubmit}>
          <h4> Sign In </h4>
          <input
            id="email"
            type="email"
            name="email"
            onChange={this.handleInputChange}
            className="validate"
            value={email}
           />
           <label htmlFor="email">Email</label>
           
           <input
             id="password"
             type="password"
             name="password"
             onChange={this.handleInputChange}
             className="validate"
             value={password}
            />
           <label htmlFor="password">Password</label>
             
           <button
             type="submit"
             name="action"
            >
            Submit
           </button>
       </form>


export default connect(null, { signIn })(SignIn);

Sign Up Component

import React from 'react';

class SignUp extends React.Component {
     constructor(props) {
         super(props);
         this.state = {
           email: '',
           password: '',
           username: '',
           name: ''
         };
         this.handleInputChange = this.handleInputChange.bind(this);
         this.handleOnSubmit = this.handleOnSubmit.bind(this);
      }
     handleInputChange(event) {
        this.setState({
          [event.target.name]: event.target.value
        });
     }
     handleOnSubmit(event) {
       event.preventDefault();
       const { email, password } = this.state;
       const userObj = {
         email,
         password,
         name
       }  
      this.props.signIn(userObj);
     }
    render() {
       const { 
          email, 
          password,
          name,
          username
        } = this.state;
       return (
        <form onSubmit={this.handleOnSubmit}>
          <h4> Sign Up</h4>
          <input
            id="email"
            type="email"
            name="email"
            onChange={this.handleInputChange}
            className="validate"
            value={email}
           />
           <label htmlFor="email">Email</label>
           
           <input
             id="password"
             type="password"
             name="password"
             onChange={this.handleInputChange}
             className="validate"
             value={password}
            />
           <label htmlFor="password">Password</label>
           <input
             id="username"
             type="name"
             name="name"
             onChange={this.handleInputChange}
             className="validate"
             value={username}
            />
           <label htmlFor="username">Username</label>
            <input
             id="name"
             type="name"
             name="name"
             onChange={this.handleInputChange}
             className="validate"
             value={name}
            />
           <label htmlFor="password">Name</label>
           <button
             type="submit"
             name="action"
            >
            Submit
           </button>
       </form>


export default connect(null, { signUp })(SignUp);

you will notice something predominant about this two components there is repetition of code. If we want to inject the onClick event handler and the onChange event handlers into arbitrary components without redundancy, an HoC is perfect.

Creating a Higher-Order Component

Before creating an Higher order component we will have to break our components into Container and Presentational components

In this world, components can be classified into those that access the global state, and those that don’t.

Container component: container component are components that have direct access to the global state i.e connected to the redux store. They also provide the data and behavior to presentational or other container components. They are often stateful, as they tend to serve as data sources.

Presentational Component: are also called functional component they r arely have their own state (when they do, it’s UI state rather than data). The y are written as functional component unless they need state, lifecycle hooks, or performance optimizations.

That said lets go back to restructuring our components. The first thing we are going to do is to convert our sign in and sign up component into functional and stateless component

SignIn Component

import React from 'react';

function SignIn(props) {
  const {oChange, onSubmit, showSignup } = props;
  return (
    <form onSubmit={onSubmit}>
        <h4> Sign In </h4>
        <input
          id="email"
          type="email"
          name="email"
          onChange={onChange}
          className="validate"
         />
         <label htmlFor="email">Email</label>

         <input
           id="password"
           type="password"
           name="password"
           onChange={onChange}
           className="validate"
          />
         <label htmlFor="password">Password</label>

         <button
           type="submit"
           name="action"
          >
          Submit
         </button>
     </form>
      <div id="message"> Don&apos;t have an account? <a
        href="#!"
        id="go-to-signup"
        onClick={showSignup}
      >Signup</a>
      </div>
    );
}

export default SignIn

SignUp Component

import React from 'react'

function SignUp (props) {
  const {onSubmit, onChange, showSignin} = props
  return (
    <form onSubmit={onSubmit}>
        <h4> Sign Up</h4>
        <input
          id="email"
          type="email"
          name="email"
          onChange={onChange}
          className="validate"
         />
         <label htmlFor="email">Email</label>
         <input
           id="username"
           type="name"
           name="name"
           onChange={onChange}
           className="validate"
          />
         <label htmlFor="username">Username</label>
         <input
           id="password"
           type="password"
           name="password"
           onChange={onChange}
           className="validate"
          />
         <label htmlFor="password">Password</label>
          <input
           id="name"
           type="name"
           name="name"
           onChange={onChange}
           className="validate"
          />
         <label htmlFor="name">Name</label>
         <button
           type="submit"
           name="action"
          >
          Submit
         </button>
     </form>
      <div id="message"> Already have an account? <a
        href="#!"
        id="go-to-signup"
        onClick={showSignin}
      >Signin</a>
      </div>
     )
}

export default SignUp;

showSignin and showSignup when clicked toggles between the signin and the signup component.

Now that we have refactored our component the next thing we are going to do is to create Higher Order Component

Higher Order Component(HOC)

import React from 'react';

function authenticate(WrappedComponent) {
  class Authenticate extends React.Component {
    constructor(props) {
      super(props);
      this.state = {};
      this.onInputChange = this.onInputChange.bind(this);
      this.submit = this.submit.bind(this);
    }

    onInputChange(event) {
      event.preventDefault();
      const inputName = event.target.name;
      const inputValue = event.target.value;
      this.setState({ [inputName]: inputValue });
    }

    submit() {
      this.props.onSubmit(this.state);
    }
    
    render() {
      const { onSubmit, ...otherProps } = this.props;
      return (<WrappedComponent
        onChange={this.onInputChange}
        onSubmit={this.submit}
        {...otherProps}
      />);
    }
  }

  return Authenticate;
}

export default authenticate;

The important part here is that the render method returns a React Element of type WrappedComponent. We also pass down the props the HOC receives.

It should be noted that in this example, we passed all props from our authenticate HOC to the wrapped component using the spread operator. This is typically advisable, as it allows the HOC to be used in place of the wrapped component.

The next thing we are going to do is to place the signin and the signup component in a single component this component will be called Index.jsx it will serve as a container component for the two components

Index Component

import React from 'react';
import SignIn from './SignIn';
import SignUp from './SignUp';
import {signInUser, signUpUser} from '../actionCreators'
import authenticate from './authenticate';

const SignUpForm = authenticate(SignUp);
const SignInForm = authenticate(SignIn);


class Authentication extends React.Component {
 
  constructor(props) {
    super(props);
    this.state = {
      userAccess: true,
    };
    this.toggleForm = this.toggleForm.bind(this);
  }
 
  toggleForm() {
    this.setState({
      userAccess: !this.state.userAccess
    });
  }

  render() {
    const { signInUser, signUpUser } = this.props;
    return (
      <div className="form-div">
        {
          this.state.userAccess ?
          (
            <SignInForm
              onSubmit={signInUser}
              showSignup={this.toggleForm}
            />
          ) :
          (
            <SignUpForm
              onSubmit={signUpUser}
              showLogin={this.toggleForm}
            />
          )
        }
      </div>
    );
  }
}


export default connect(
 null, 
 {signUpUser, signInUser}
)( Authentication);

Here we have abstracted all our logic into the HOC and passing it down to the wrapped component which is our SignIn and SignUp components. we create a higher-order component by just passing in a component to wrap.

const EnhancedComponent = higherOrderComponent(wrappedComponent)

This can now be used anywhere wrappedComponent was used previously, as it returned a component wrapping wrappedComponent.

Keep in mind — in most of cases HOC will return something that looks like original component. To achieve this you often have to pass HOC propTypes and the rest of static methods, which may exists in original Component.

You can find more information about this at the end of Facebook`s article.

Conclusion

HOC is awesome, they are at heart a codification of a separation of concerns in components in a functional way.

I hope that after reading this post you know a little more about React HOCs. They are highly expressive and have proven to be pretty good in different libraries.

Discover and read more posts from Chinonso Johnson
get started
post commentsBe the first to share your opinion
Show more replies