React Hooks — Why and How
Recently the React team decided to bring Hooks into our lives. I am pretty sure that you have heard of Hooks, though there are still a lot of people that haven’t taken the time or gotten the chance to experiment with them.
In this post, I will be showing you reasons why you should be using the new Hooks in your future work. Furthermore, I will be explaining how you can use them best and hopefully motivate you to build awesome things with Hooks.
In the code examples below you will see that I am using
<></> a couple of times. These are Fragments. In case you are not familiar with these, go check them out! They are awesome.
TL;DR: Hooks give us the possibility to extract stateful logic which before could only be used in Class components and allow us to test this logic separately and reuse this logic.
Mixins are no more
Back in the days in order to reuse stateful logic, there were mixins.
There were a couple of problems with mixins:
Mixins introduce implicit dependencies — In short, this means that both your component could rely on a method defined in your mixin (or the other way around) and there is no way to enforce this method to be present.
Mixins cause name clashes — If you have two mixins that both define a
handleKeydown method, you cannot use both and you cannot define a method with the same name on your component.
Eventually, mixins became considered harmful when we learned about Higher-Order Components
Higher-Order Components pattern
The HOC pattern is pretty powerful yet so simple and elegant.
It’s just a function that returns your component wrapped with another component.
Although it’s a great and powerful way there are some downsides too, for example using too many HOC’s could get you in wrapper hell.
There are entire libraries build around this pattern, like Recompose. Recompose made it easier to turn a dumb functional component stateful, simply by composing it with a couple of HOC’s — which in turn can get you to wrapper hell.
The problem with classes and current lifecycle implementations
As you can see in the example above we have defined our lifecycle methods:
These lifecycle methods can of course only be defined once per component, which means that we have non-related effectful code within each one of these lifecycle methods, i.e.,
fetchUser and the
window.addEventListener have nothing to do with each other, yet they live the same code block. This can make class components messy and decreases code readability as complexity within your component grows.
Hooks to the rescue
With the introduction of Hooks, the use of functional components get’s promoted — This does not mean that classes are being removed from React.
Hooks are completely opt-in. You can try Hooks in a few components without rewriting any existing code.
What do Hooks solve? In class components it’s hard to reuse stateful logic between components and complex class components become hard to understand and even harder to read.
Let’s say I want to have a controlled input within my component. I can create this controlled input by implementing the
useState Hook which will store the current value entered in the input and provides us with a method to update the state:
Amazing, right? we have just added a state to our functional component!
But what if we need multiple states? Don’t worry, you can!
useState is amazingly simple to use and can be passed any datatype to store in the state, for example, objects. The difference between the original class component method
setState and the method provided by
useState to set the state is that it does not merge objects.
In order to deal with a complex state like a nested object, we can use
Everyone that has worked with Redux before will notice similarities in syntax and usage:
useEffect is a lifecycle Hook for functional components that combines
useEffect is named
useEffect because this is where you carry out your side effects, e.g., ApiFetching, Subscriptions or Events.
Even though it is a lifecycle method like the others, in a way it’s better than the rest. The reason for it being better is that you can make use of
useEffect multiple times within a single component, which allows you to split up and keep related logic grouped together and maintaining readability of your code.
The most simple example of using
useEffect, is to make our component listen to the keydown event.
As you can see we return a function inside of our effect which removes the eventListener,
useEffect calls a returned function whenever it is time to clean up, like when a
componentWillUnmount is called.
The second parameter in
useEffect allows us to only run the effect when specific values have changed like in the following example, we only run the effect whenever the userId has changed.
Optionally the second parameter can also just be an empty array, in this case it will only execute on
componentWillUnmount and the effect won’t run on
Let’s put useState and useEffect to use
Now that we’ve discussed
useEffect I thought it would be nice to put some of these to use and refactor a class component to a functional component. Furthermore we will utilize the Hooks API and create a custom Hook that enables us to easily implement handling keyboard shortcuts on component level.
Let’s say we have a clock component which shows the current time and updates every second. Here we go!
We have a simple class component with a state containing the current time, a
componentDidMount lifecycle method that creates an interval that updates the current time in the state every 1000 milliseconds and a
componentWillUnmount which clears the interval when we unmount the component… Pretty straight forward.
Now the part that we actually want to refactor is:
We want to refactor the lifecycle methods above to use a single
useEffect and the state to use
useState so that we can refactor our class component into a functional component.
How would we do this using Hooks?
Let’s get started by importing both
useEffect. Let’s then turn our component into a functional component.
Now our variable time is undefined so let’s create a state called time, we will be using
useState with a default value which is the current time.
We now have a state within our functional component that stores our current time! As our next step we update this every second or so as we did in our class component, let’s implement the
Remember that useEffect acts as
componentWillUnmount. In our situation, we do not want our effect to run again on
componentDidUpdate so we pass an empty array as the second parameter to only run our code on
Adding to that you can see that we return a function from our effect and that we call clearInterval from within, this is us cleaning up, remember? That method gets called whenever React thinks it’s time to clean up like
Here’s a working example of our refactored Clock component using Hooks
The code looks different, shorter and much cleaner but does exactly the same, take a look at the before and after:
Now that we’ve gone over the basics, let’s go ahead and create our own custom Hook.
According to the React Hooks documentation
use” and that may call other Hooks
Of course you can name your Hook whatever you like. It is just best practice to start your hook with “use”. If you start your custom Hook’s name with “use”, React’s eslint-plugin-react-hooks is able to find bugs in the code using Hooks.
A custom Hook is a great way to create reusable logic, in this example we will create a custom Hook that allows us to easily add keyboard shortcuts on component level.
Imagine we have a list of some kind, in this example, we will use a list of people portrayed in the series “Silicon Valley”, I have set up a Codesandbox that you can use to get started. As you can see there is an active item highlighted, you can click any of the other items to highlight the item.
Now let’s create and implement our custom Hook and Hook up the arrow keys so that we can navigate through this list using our keyboard.
We want to create a Hook that:
- Stores the active keys in the state;
- Sets an active key in the state on keydown;
- Removes an active key from the state on keyup;
- Returns a method to check for active shortcuts in a human-readable way, for example, “shift+enter”;
Setting up the basics
Next we will implement some logic to add an active key to our state, remove it from our state whenever it’s no longer active, create our
isShortcut helper method and attach our event listeners, let’s also not forget to clean up!
useShortcut which adds two event listeners, one to handle our keydown and one to handle our keyup event.
On keydown, we are adding the newly pressed key to our
activeKeys state, if it isn’t already active and on keyup, we remove the released key from the
We’ve also created a helper method called
isShortcut which accepts a string like “shift+enter” and splits these into separate keys after which it will check if all keys are currently present in our
Our method returns an array containing our
activeKeys array and our helper method
isShortcut. And whenever React finds it’s time to clean up we remove our event listeners.
Great! We have just created our own custom Hook, let’s put it to use in our Codesandbox from earlier where we display a list of people.
We can use our new custom Hook by importing it and adding a
useEffect to our component from which we call our helper method like previewed in the following example:
I have created a working version in Codesandbox so you can see the end result:
Now I have got to be honest, our
useEffect looks pretty messy… Remember that I told you that you can use
useEffect as many times as you want inside a component? We could split this up into 3 different
useEffects but instead, I think we should update our custom Hook and create a custom effect which we can just implement.
What does a custom effect look like?
And behind the scenes, it’s not a lot more than a method that uses our custom Hook and
useEffect and calls our callback method whenever a shortcut matches, here’s the source:
So to put it all together, I have created a Codesandbox that uses our new custom effect, take a look at the source!
Much cleaner eh..?
I hope you have enjoyed reading about the Hooks that we have discussed.
The Hook we have created in the above written examples is available on NPM.
I will soon be posting a next part in which we will go over a bunch of other Hooks.
Thanks for reading :)