Thinking with custom React hooks
Custom react hooks can be quite unintuitive at first glance. Let's look at how they compare to objects and classes.
Class components and classes in general have a bit of a bad rep when it comes to frontend development. Javascript was never made to be object oriented and in its current state it has several issues. That being said, classes are an intuitive way to structure code, and in this article we will explore how to emulate the best parts of objects and classes using custom react hooks.
With the introduction of react hooks in 2019, class components were pretty much made obsolete, but they take some getting used to. Thinking with hooks forces you to be more aware of the inner mechanisms of React, but once you get used to them, there's no going back. I think most people understand the idea of the basic hooks like useState and useEffect, but then you read about custom hooks, which at least for me, took some getting used to.
So what are custom react hooks, and why should you use them? Ideally it's a way to compartmentalise react state and effects, as well as allowing it to be used in multiple components. "Ok, so when would I do that?" you say? All the time.
To illustrate what I mean I have created a custom timer hook, but before looking at the code, let's look at how it's used.
Looking at this I don't think I need to point out that it might as well have said const timer = new Timer();
To me this is what I want to achieve when I create a custom hook. An interface to get and manipulate the state of something without needing to know about the inner workings of the code to do so. I can hand this off to a colleague and with only IDE suggestions, they can figure out how to use it. Let's have a look at the hook itself.
This is just one solution to creating a timer in react, I'm sure there are a million ways to make this better, but it does the job. The thing I want to highlight is what is in the return statement. The only things I chose to give to the consumer of the hook is the actual value of the timer, and ways to interact with it. If something else is needed for your application, maybe a way to set the state of the timer, we can just add it to the return statement.
One thing I did decide to include was a way for the timer to interact with its consumer through the optional onComplete argument. Which is just one of many ways to have your hooks communicate with the rest of the app.
And here is the small app i created using this hook.
The key to working with hooks is thinking in terms of compartmentalisation. All functions that want to mutate the timer state, belong in the timer hook, and we return only those functions that are needed by the consumer of the timer (as they would have belonged in the timer class). Do this often and early for when you feel a part of your code revolves around the same state. It's easy to forget that the true difficulty with React in general, is keeping your code maintainable and readable as your application grows. It's easy to let custom hooks become an after the fact thing, where you throw in a 50-line useEffect into a custom hooks with 10 arguments and call it a day. There are no shortcuts to clean code, but thinking with hooks early will help you on your way.