React Modular Design with Higher Order Components — Part 1
TLDR: React JS uses Higher Order Components (HOC)s as a design pattern for adding extra functionality to components. By abstracting the functionality into a wrapper, you are able to keep a smaller set of base components. This makes your codebase more modular.
A common theme of front end development is the creation of reusable, modular components. When someone says that a component is modular, what they really mean is that the component has the necessary basic functionality to accomplish its goal, with the ability to be modified to fit a specific use case.
A frequent example of a modular component is a button. While buttons may have different colors, shapes, sizes, and call different functions, they can be broken down into modular components. If you want a uniform UI experience for users, you may create a base, reusable button with the same shape and user initiated pseudoclass properties.
If you know that your button is going to make a call to the user database to add a record, or modify an existing one, you may even put functions inside the button component that accept the user id and action to perform as props to trigger calls to the database. While this is a great approach to get your buttons more modular, this does not solve the issue of having a number of buttons that accept inputs for different actions, but are still hardcoded for specific data sources or state inputs to retrieve.
This is where higher order components (or HOCs) come into play. As the React docs state:
Concretely, a higher-order component is a function that takes a component and returns a new component.
Taking this a step further, a higher order component is a function that accepts a component as an argument, and returns a new component with extra functionality. HOCs work similarly to extending classes as they take a base component with its basic functionality, and add props and methods to extend its functionality for specific use cases.
The ultimate goal of a HOC is to create a component that extracts all business and state logic from the wrapped component. By doing this, all the communications with the API and manipulation of data can be done a single time with the end results being passed as props to the wrapped component. HOCs both simplify calls and repeated business logic between multiple components, and also allow wrapped components to be purely presentational.
While the goal is to pull all business logic out, sometimes we can only pull out a set number of shared calls and lifecycle methods from a set of components. A good example of this is a submit form. While you can create a modular submit button that will always look at the components state for the information to post to a database, what if you want that information to show up on a validation component or a presubmit component?
This is where HOC’s come into play. Instead of writing repeated functions to access state on user events, you can create an HOC function that will take your in your three different types of components and give them the same setup functions for reaching for that state triggered on events. This will help you avoid creating extra basic components that will clutter your component library.
HOC’s help programmers adhere to DRY programming (Don’t Repeat Yourself) by separating business logic that multiple components may be using into a single function that can be reused in each component. This decoupling allows you to keep a smaller library of more generic components as the HOC’s will transform each component for the specific use case.
In my next article I will discuss the use of Redux’s HOC connect function and how I am using it to access the store to get the global state of my NASA gallery app as well as call action creators with store.dispatch() to update the global state from my components.
Sources