React hooks focus on separation
If you’ve been using React for a while, you might encounter container components and presentation components, or smart components and dumb components. These terms describe the pattern that separates the UI layer from the logical layer of the React component.
Separating UI from business logic isn’t unique to React: Separation of concerns is a design principle that dates back to the 1970s. For example, it is common practice to separate the code that accesses the database from the business logic on the back end.
So in React, we solve this problem by creating a container component that contains all the logic, which then passes data to the presentation component via props.
With the introduction of React Hook, there’s a new way to do this: use custom hooks.
Why do we separate logic from components?
Before we do that, we need to know why we want to separate the logic from our React component.
Organizing our code so that each function or component does only one thing has the advantage of being easier to change and maintain (what Dave and Andrew call “orthogonality” in their book “The Pragmatic Programmer”).
Applying this to reactions means that our components look cleaner and more organized. For example, we don’t need to climb over a logical wall before editing the UI.
Organizing your code like this not only makes it look better and easier to navigate, but it also makes it easier to change, because changing the hooks doesn’t affect the UI, and vice versa.
The tests are also easier to access: we can test the logic separately from the UI if we wish. For me, however, the most important advantage is how this approach organizes my code.
How to decouple logic with React hooks
To decouple the logic from the component, we will first create a custom hook.
Let’s take this component as an example. It computes the value of the base and exponent:
256.00
You can find the full source code here.
The code looks like this:
export const ExponentCalculator = () = > {
const [base, setBase] = useState(4);
const [exponent, setExponent] = useState(4);
const result = (base ** exponent).toFixed(2);
const handleBaseChange = (e) = > {
e.preventDefault();
setBase(e.target.value);
};
const handleExponentChange = (e) = > {
e.preventDefault();
setExponent(e.target.value);
};
return (
<div className="blue-wrapper">
<input
type="number"
className="base"
onChange={handleBaseChange}
placeholder="Base"
value={base}
/>
<input
type="number"
className="exponent"
onChange={handleExponentChange}
placeholder="Exp."
value={exponent}
/>
<h1 className="result">{result}</h1>
</div>
);
};
Copy the code
This may seem good enough, but for the sake of this tutorial, imagine there is more logic here.
As a first step, we’ll move the logic onto a custom hook and invoke it from within the component.
const useExponentCalculator = () = > {
const [base, setBase] = useState(4);
const [exponent, setExponent] = useState(4);
const result = (base ** exponent).toFixed(2);
const handleBaseChange = (e) = > {
e.preventDefault();
setBase(e.target.value);
};
const handleExponentChange = (e) = > {
e.preventDefault();
setExponent(e.target.value);
};
return {
base,
exponent,
result,
handleBaseChange,
handleExponentChange,
};
};
export const ExponentCalculator = () = > {
const {
base,
exponent,
result,
handleExponentChange,
handleBaseChange,
} = useExponentCalculator();
// ...
};
Copy the code
We can move this hook into a separate file to separate concerns more dramatically.
Additionally, hooks can be further separated into smaller, reusable functions. In this case, we can only extract and calculate the exponent.
useExponentCalculator.js
const calculateExponent = (base, exponent) = > base ** exponent;
const useExponentCalculator = () = > {
const [base, setBase] = useState(4);
const [exponent, setExponent] = useState(4);
const result = calculateExponent(base, exponent).toFixed(2);
// ...
};
Copy the code
Testing these functions is much easier than testing the entire component code in the first example. We can test them with any Node.js test library, and it doesn’t even need to support the React component.
Now, we have frame-specific code (React) in our component and hook code, and our business logic resides in the different functions we define later (which are frame-independent).
Best practices
named
I like to name my custom hooks after the name of the component, as a concatenation of use and component names (e.g. useExponentCalculator). Then I call the same hook as the file.
You may want to follow different naming conventions, but I recommend consistency in your project.
If I can reuse parts of a custom hook, I usually move it to another file under SRC /hook.
Don’t overdo it
Try to be pragmatic. If the component has only a few lines of JS, there is no need to separate the logic.
CSS-in-JS
If you are using the CSS-in-JS library (UsStyle), you may also want to move this code to another file.
You can put it in the same file as the hook. However, I prefer to keep it on top of components in the same file, or move it to my own file if it grows too large.
conclusion
Whether or not you think that using custom hooks improves your code is ultimately a matter of personal preference. If your code base doesn’t contain a lot of logic, the benefits of this pattern won’t be relevant to you.
Custom hooks are just one way to add modularity; I also strongly recommend breaking up components and functions into smaller, reusable chunks whenever possible.
This topic is also discussed more generally in the practical Programmer. I’ve written an article covering books on my favorite topics, so be sure to check it out if you’re interested.
link
Here are some articles I found helpful in researching this article:
-
Separation of concerns with custom React hooks – Arek Nawo
-
Presentational and Container Components – Dan Abramov
This is an excellent article on the container pattern and is still useful for class components in React.
Some other articles I wrote that you might like to read:
-
Form validation with React Hooks WITHOUT a library: The Complete Guide
I wrote this article to create my own form validation without using any libraries. I find that one library is often too many for my use cases, and it only takes a few lines of code to create my own.
-
How to Make your React App a Progressive Web App (PWA)
A detailed guide to all the steps you need to take to turn your React app into a PWA; It’s easier than most people think.