The Need for Recompose
Recompose is a React utility belt for function components and higher-order components. Think of it like lodash for React.
Recompose allows you to enhance your functional components by adding state, lifecycle methods, context, among other things.
Best of all it allows you to have clear separation of concerns – you can have the main component responsible exclusively for layout, a higher-order component responsible for handling form inputs, another one for handling form validation, another one for submitting the form. And it is very easy to test!
The Elegant Functional Component
Let’s see what can be done about the bloated class component.
Step 0. Install recompose.
Let’s add recompose to our project.
yarn add recompose
Step 1. Extract input form state.
We’ll be making use of the
withStateHandlers higher-order component from recompose. It will allow us to isolate the component state from the component itself. We’ll use it to add form state for the
confirm password fields, as well as event handlers for the above-mentioned fields.
withStateHandlers higher-order component is very simple – it takes the initial state, and an object containing the state handlers. Each of the state handlers will return new state when called.
Step 2. Extract form validation logic.
Now it’s time to extract the form validation logic. We’ll be using
withProps higher-order component from recompose. It allows adding any arbitrary props to an existing component.
withProps to add the
confirmPasswordError props, which will contain errors if any of our form fields are invalid.
One should also note that the validation logic for every one of the form fields is kept in a separate file (for better separation of concerns).
Step 3. Extract form submission logic.
In this step we’ll extract form submission logic. This time we’ll make use of the
withHandlers higher-order component to add the
Why not use
withProps as before? Having arrow functions in
withProps significantly hurts performance — a new instance of the function will be created under the hood every time, which will result in useless re-renders.
withHandlers is a special version of
withProps , which is intended to be used with arrow functions.
handleSubmit function takes the
passwordError , and
confirmPasswordError props passed down from the previous step, checks if there are any errors, and if not posts data to our API.
Step 4. Here comes the magic.
And finally we combine the higher-order components we’ve created into a single enhancer that can be used on our form. We’ll be using
compose function from recompose which enables combining multiple higher-order components.
Note how elegant and clean this solution is. All of the required logic is simply being added on top of another to produce one single enhancer component.
Step 5. A Breath of Fresh Air.
Now let’s take a look at the
SignupForm component itself.
The new refactored component is very clean, and does only one thing – rendering the markup. Single responsibility principle states that a module should do one thing, and it should do it well. I believe that we have achieved this goal.
All of the required data and input handlers are simply passed down as props. This in turn makes the component extremely easy to test.
We should always strive for our components to contain no logic at all, and only be responsible for rendering. Recompose allows us to do just that.
Project Source Code
If you run into any issues following along, you can download the entire project from github.
Bonus: optimizing performance with pure
pure, which is a nice higher-order component that allows us to re-render the components only when needed.
pure will ensure that the component doesn’t re-render unless any of the props have changed.
We should always follow the single responsibility principle, and strive to isolate logic from presentation. We do this by first of all outlawing class components. The main component itself should be functional, and should only be responsible for rendering the markup and nothing else. All of the required state and logic is then added as higher-order components.
Following the above rules will make your code clear and clean, easy to read, maintainable, and easy to test.
An article on testing higher-order components is coming soon.