Reusing React Component logic with Higher Order Component
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 handlers
accessing 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'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.