React Hooks, besides their other advantages, made the code shorter and much easier to read. However, with some components, the growing amount of useEffect and useState can be still very confusing and I was quite at loss for a while about how to organize all these hooks.

Let’s build a simple application to demonstrate the issue and my resulting refactoring.

AutoCounter

We’re building a simple component that constantly auto-increments its value (via setInterval) and you can make the speed at which it runs, faster or slower.

We’ll start with the basic UI code with the counter and 2 buttons:

Next, let’s use useState to keep the speed value, and the slower/faster callbacks to control it:

Last, let’s add the setInterval code. It is a bit complicated since every time the speed changes, we need to destroy the previous interval and create a new one (If it still looks confusing, I highly suggest to read Dan’s awesome overview of useEffect):

And here it is altogether as a single component:

See it running in CodeSandbox

To be fair, it isn’t too bad in this simple component. However, even in this basic form, it is rather confusing. We got various bits of state intervened with logic and callbacks and it is all at the scope of the UI code. How should we organize it?

Previously, we’d organize our code using functions. Well, now we can organize it the same way, except now these functions are custom hooks.

AutoCounter with Custom Hooks

Custom hooks are usually discussed as a way to share logic between components, however, as long as we don’t share them, they are also perfect for organizing the logic of our own, single component.

Let’s identify the logic parts that we have in our component, the first one would be the speed and its controlling functions. Let’s encapsulate them in a custom hook:

Note that even though we captured all the speed-related logic and return most of its data, we don’t return setSpeed back from it, thus, safely encapsulating it inside our hook.

Next, let’s handle the auto-counter part. Since the useEffect that we use depends on the speed, then it must be the parameter of the new custom hook:

With these 2 custom hooks, our new component code is now much cleaner and looks like so:

See it running in CodeSandbox

Summary and TL;DR;

Custom hooks are not just for sharing components logic. You can make a “private” custom hook, that is a part of your component used only for organizing your component logic.

Extract the logic of your component to custom hooks to make your code more readable.

Custom hooks can accept data from other hooks as function arguments.

Custom hooks can return callbacks to expose functions that are inside them.

If your custom hook is too complicated, you can even break it down to sub-custom hooks.

Note that these custom hooks should abide by all the regular rules of hooks.

What do you think? I would be happy to read your comments to know whether you agree with my statements.