The three pains above were described as a symptom of a bigger problem. That is, React doesn’t provide a stateful primitive simpler than a class component.

To fix this issue, the React team introduced Hooks.

Now, why are they called Hooks? What are they? What do I win by using them?

React Hooks

Hooks are functions exposed by React that you can use from inside your functional components to access some of its features — like state for instances.

Or, if you want to explain it on a one-liner:

Hooks lets you hook into React features from function components

So with Hooks, React now has a new stateful primitive that improves code reusability without changing the component hierarchy, by being able to create custom Hooks. Let’s split your components into smaller ones where the related logic is all coupled together, improving code readability, and use other React features without classes. All this without causing breaking changes!

After that summary, let’s do a before Hooks and after Hooks with the basic Hooks.

useState

Before

As you can see above, we have a “simple” Counter component. Well, “simple” given the amount of boilerplate, we need to increment the counter — this passes by having this all around, binding functions, and all of it leads to some lines of code increase.

Now

The first thing we see is that we removed unnecessary boilerplate: no constructor, no unnecessary binds, and overall half the number of lines of code.

Now what we do is, inside our function, we tap into the React useState Hook. We pass it an initial value, and it returns us the state variable and a function we can use to update the state. Pretty simple right?

useState “Gotchas.”

When well call the function to update the state, it enqueues a re-render of the component.

You can have multiple state variables with useState

Contrary to the previous setState, useState replaces state objects instead of merging them.

If you are using an object as state, when you are setting state, you can spread the state to avoid losing properties.

useEffect

Before

The example above fits precisely with the example on the Giant and confusing components section. So let’s avoid repeating text and go forward to learn about useEffect.

Now

Passing from class components to functions, the first thing we get rid of is lifecycles. So, if we don’t have lifecycles, how can I run stuff depending on changes to state, or on the component mount for instances?

Given this, React introduced useEffect. useEffect lets us run side effects on our function components. So it’s a shift from thinking in lifecycles to thinking in effects. It may seem a bit confusing explained like this, but we’ll understand it going forward.

Look at this syntax above. What we see here is that the useEffect accepts two arguments being the first a function to be run as an effect and the second one an array of values, which is called dependencies array. Think of the dependencies array as a group of variables that React is looking at, and when one of them changes, React will say: “Hey, on your dependencies has changed. Run this effect, please”.

With this knowledge under our belt, let’s look at the first useEffect block.

What happens on that first effect is, every time our title variable changes, then React will run an effect responsible for changing the document title. With what is a simple effect we just covered two lifecycles and reduced a great amount of code.

Let’s look at the second effect as it is a bit different from the other one. The first thing we may see is the dependencies array is empty — that means that that Effect will only be run once given that it doesn’t have any dependencies to watch. The second thing is that return, this return is what is called a cleanup, and will be run when our effect is being cleaned up (you can compare it to componentWillUnmount). So what we are doing on this effect is, when our component is first rendered, we will subscribe to a store, and when it’s time to clean up that effect, then we will unsubscribe from that store.

With useEffect, you can couple related logic, making the code more readable and more comfortable to debug and do several effects according to our needs.

useEffect “Gotchas.”

If no dependencies array is passed the effect will run every re-render

If an empty dependencies array is passed we aren’t watching any changes so the effect will only run on the first render.

You can return a function from our effect that will be responsible for cleaning it up.

Be careful with objects inside the dependencies array as if they aren’t memoized then the references will change on every re-render forcing that effect to run unexpectedly.

useContext

Before

React team created context to have a way to pass data between a component tree without having to prop drill through every level. This solution passed by declaring a context provider at a top-level and a context consumer on the components that needed to consume that context. The next example shows how we could consume a context.

It may seem pretty simple, right? But let’s imagine that on the same component, we need to consume more than one context. We will end-up having our render tree wrapped with multiple consumers. This leads to the above mentioned “wrapper hell.”

Now

With the introduction of useContext now, you can access any context data at our component top level and use those props freely around our code. There are no more “wrapper hell,” and our render is readable and only consists of the presentational part of our component.

useContext “Gotchas.”

A component that is subscribing to changes in context re-renders when any of the context values change.

Now that we reviewed the basic Hooks, let’s delve into some additional Hooks.

Additional Hooks

Why additional Hooks, you ask. That is because they either variate from the basic ones or are just needed on some edge cases. Here we do a brief description of each.

useReducer

( If you have used Redux you may be already familiar with this concept)

useReducer is a useState alternative that accepts a reducer and an initial state and returns the current state and a dispatch. The following example comes straight from the React docs and perfectly illustrates how to work with useReducer.

useRef

useRef creates a mutable reference on its .current property given an initial value. You may already be familiar with the ref as a way to access the DOM, with useRef you can store JavaScript objects to guarantee that you receive the same object on every render.

useMemo

Allows you to memoize expensive functions so that you can avoid calling them on every render

Since useMemo tends to be a bit confusing, I’m going to try to clarify it. Let’s look at the following example.

Let’s consider we have an expensive function computeLetterCount . If we just did a simple assignment of its output to the letterCount variable, then every render of our component that function would recompute. This recomputation would lead to a slowing down of our component. Entering useMemo to the rescue — by wrapping our function call with useMemo and passing it a dependencies array (remember useEffect?), then React makes sure that the function call only recomputes when a dependency changes. So, now, our expensive computation only happens when that word changes and avoids slowing down our component on every render.

useCallback

It allows you to memoize functions so that you can avoid creating them on every render.

useCallback is another Hook that is more simple to understand if we go step-by-step. Let’s imagine we have a component that uses React.memo to guarantee that the component only re-renders when the props change.

If we create a function, every time that component renders, then it creates a new reference for it. What will this cause?

If we are passing that function to our component that is wrapped by React.memo, then every re-render that function is considered a new one, leading to unnecessary re-renders happening on that component.

useCallback guarantees that while any variable on its dependencies array (remember useEffect again?) doesn’t change, then React gives you precisely the same reference for that function allowing for React.memo to detect that there weren’t any changes and not re-rendering that component.

Rules of Hooks

Ony call Hooks at the top level

Hooks shouldn’t be called from inside conditions, loops, or nested functions. That is because React relies on the order in which the Hooks are called, and if you skip a Hook update, then that order would be changed and cause erratic behavior.

Only call Hooks from React functions

This one is straightforward — Hooks may only be called from React function components or custom Hooks.

Conclusion

With this article, we learned what motivated the appearance of Hooks and why they allow us to write React code the way it always supposed to be — reusable.

Despite being around for a year by now, Hooks are nowhere as close to the expected potential. Many people still fear to learn Hooks and I hope this article gave you all some insights into them and you decide to give them a shot.

I hope you enjoyed and stay tuned because some brand new Hooks are coming (I’m looking at you Concurrent Mode).

Have a great weekend!