useAbstraction: React Hooks as a Tool for Abstraction and Encapsulation

David Good
May 15, 2020 · 4 min read
Rustic wooden door, nearly indistinguishable from planks of wood
Rustic wooden door, nearly indistinguishable from planks of wood
Photo by Jonathan Ybema on Unsplash

React Hooks have taken the web development community by storm. A lot of people talk about how hooks are great. How hooks allow them to do things they never could do before. How they enable code sharing and reduce the size of your codebase. But I haven’t heard anyone talk about custom hooks as a tool for abstraction and encapsulation.

Abstraction is the ability to engage with a concept while safely ignoring some of its details… If you refer to an object as a “house” rather than a combination of glass, wood, and nails, you’re making an abstraction. If you refer to a collection of houses as a “town,” you’re making another abstraction. ~ Code Complete by Steve McConnell

Let’s take a common scenario where a React component needs to fetch some data. You’ll need to track the state of the fetch and render appropriately: a loading indicator, an error, or the client details based on the fetch results).

function Client({ id }) {
const [client, setClient] = React.useState(null);
const [status, setStatus] = React.useState("init");
React.useEffect(() => {
async function doFetch() {
try {
setStatus("loading");
const response = await fetch(...);
const clientResponse = await response.json();
setClient(clientResponse);
setStatus("success");
} catch (e) {
console.error(e);
setStatus("error");
}
}
setClient(null);
doFetch();
}, [id]);
return (
<>
{status === "loading" && <div>Loading...</div>}
{status === "error" && <div>An error occurred</div>}
{client && <div>Name: {client.origin}</div>}
</>
);
}

Let’s focus on just the call to React.useEffect. What can we say about it? Well for starters, it’s a distraction. When reading through the component, it really breaks up the flow of the code for the reader. But why? One reason is that it’s at a different level of abstraction that the surrounding code. One of the principles of clean code is that one function should be at one level of abstraction (details here), and this component does not adhere to that principle.

Let’s assume now that we don’t know much about React. From our naive point of view, we can see that in the code surrounding the call to useEffect, we’re “using state”, and below it we’re returning some HTML. And then in the middle, there’s all this noise about fetching, JSON, a URL, a try-catch block… See the point?

What if the distracting call to useEffect in the middle of our component were at the same level of abstraction as useState? That is to say, what if we could hide away all the details about fetching, and just use something? Well of course we can do just that! We can replace the useEffect call with a custom hook:

const { status, data } = useClient(id);

This also allows us to eliminate the tracking of the fetch status in the Client component, which makes sense because such low level details should not be a concern of the Client component. With that change, our Client component is simplified to this (what an improvement!):

function Client({ id }) {
const { status, data: client } = useClient(id);
return (
<>
{status === "loading" && <div>Loading...</div>}
{status === "error" && <div>An error occurred</div>}
{client && <div>Name: {client.origin}</div>}
</>
);
}

So what does useClient actually do? To further demonstrate my point, let me first tell you that it doesn’t matter. We can infer that it takes a client ID and returns the status and the client data. But we shouldn’t need to be concerned with what’s going on inside of useClient. That’s because the details of “using a client” are abstracted away by the custom hook. Furthermore, the logic is encapsulated in the custom hook: it has a simple interface (a single function), and we must use that interface assuming the hook doesn’t export any additional functions (it shouldn’t). If you’re still curious about the code, there’s a link below.

Encapsulation picks up where abstraction leaves off… encapsulation is a way of saying that you can look at the outside of the house but you can’t get close enough to make out the door’s details… you’re not allowed to know whether the door is made of wood, fiberglass, steel, or some other material, and you’re certainly not allowed to look at each individual wood fiber. ~ Code Complete by Steve McConnell

Let me leave you with this which ties together abstraction and encapsulation:

…without encapsulation, abstraction tends to break down. In my experience, either you have both abstraction and encapsulation or you have neither. There is no middle ground. ~ Code Complete by Steve McConnell

Code from this post is available on CodeSandbox.

David Good

Written by

Software engineer crafting full-stack, cloud-native solutions for enterprise

The Startup

Medium's largest active publication, followed by +773K people. Follow to join our community.

David Good

Written by

Software engineer crafting full-stack, cloud-native solutions for enterprise

The Startup

Medium's largest active publication, followed by +773K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store