Do these scenarios sound familiar to you? You need a loading state, a success and error state from the fetch API, a mechanism to cache data, to check the current mouse pointer, and on and on, all in different components. We repeat this logic multiple times across multiple components. If you take a closer look, you'll find that each scenario requires you to have its own state, which depends on a life cycle method like useEffect. You can go ahead and write the logic for this in each of your components, or you can create a common wrapper that has its own state variables and useEffect that helps to maintain its own isolated state. In other words, you can write custom hooks.
In this article, we are going to unravel all of the mysteries related to custom hooks and take a look at a good number of examples of creating our own custom hooks.
Without further ado, let’s get started!
Read the below articles to get the most out of this article:
What are Custom Hooks?
React v16.8 introduced hooks, wrappers that help to encapsulate repeated stateful logic so that it can be used and shared later across multiple components.
Hooks allow you to use all class-based components’ life cycle methods in function-based components. They let you use useState and other React features without a class.
Having said that, React allows you to create your own custom hooks. A custom hook is a way to share stateful logic across components. We can also follow HOC and render props patterns to share this stateful logic across components; custom hooks are just another way to do it.
How to Create a Custom Hook
To create your own React custom hook, we have to follow certain guidelines. Here is a summary of these rules:
- Your custom hook should start with the keyword use, for example, useAuthState, useFetch, etc.
- Hooks should be called at the top of the component. We should avoid using hooks inside of a condition or a loop because React will be uncertain about the order of execution of the hooks. React knows about each hook and its associated value via the order of execution of hooks during rendering. It’s expected that at each render, the order of hooks remains the same. In this way, React preserves the state.
- Hooks should be called from a function-based component.
- You can call one hook from another hook.
- Every call to a hook gets an isolated state.
Now that we know some basic rules of hooks, let’s create a simple example:
This is one of the more basic React custom hooks examples. In this hook, we do the following things:
- We return an access token, isError, and error state.
- The purpose of this hook is to return the access token from the local storage or fetch a new one if the access token is not present in the local storage.
- We have named this hook useAuthState. This satisfies the rule that the name of the hook must start with use.
- If you carefully observe the code inside this hook, you will find that we need this each and every time we need to use the access token.
- This can create a lot of redundant code pretty quickly.
- To avoid this, we can make use of such hooks and use this logic in whichever component we require.
- While the hook is in use, it gets its own isolated state.
Using this component is also simple. Just import the hook as follows:
Now that we have a basic understanding of custom hooks, let’s dive deep into some more React custom hooks examples.
Creating a Custom Hook to Fetch Data
There might be scenarios where you want to fetch data from an API, but you also need the following data:
- Is data fetching? (e.g., isLoading)
- Was data fetched successfully?
- Was fetching the data unsuccessful, throwing an error?
In all of the above scenarios, you would do something like this:
You would make use of multiple useStates and one useEffect to get the data on page load. Imagine you are repeating this multiple times across multiple components.
In such cases, it is suitable for us to create a hook that will abstract this stateful logic. Then we can simply get these values/states as a return value of the hook.
Here we go: we’re going to call useFetch as a hook that will fetch the data for us from the given URL/API.
We just wrapped the above code inside a custom React hook function. There are a couple of things to note about our custom hook:
- It has multiple useStates, such as isLoading, isSuccess, etc., that tell us the state of the API at different phases.
- isLoading - This is set to true at the start whenever we start fetching the data inside the useEffect. Once the data fetches or an error throws, we set its value to false. We are doing this because we need a way to know that the hook is doing its work. While the hook is working, we can show a spinner on the UI with the help of this toggle. Its default value is false.
- data and error - These two states, as the names suggest, store the data and the error that the fetch API throws.
- isError - This toggle state shows whether an error has occurred or not. Its default value is false, but if we get an error during fetch API, then we set its value to true.
- isSuccess - This toggle state is set to true whenever the data from the fetch API fetches successfully. Its default value is false.
- In the above hook, we introduced a setTimeout API inside the useEffect because we want to display the loading state. Since the data is fetched quickly, we introduced the setTimeout API to showcase the loading state.
The code below demonstrates how we consume this hook:
A Custom Hook to Track Mouse Position
There might be certain scenarios where you might want to track your mouse movement from the edges of the browser inside a specific div or you want to track it over the entire page. In any case, custom hooks can return the x- and y-coordinates along with a handler function that will manage the track of the mouse event inside an element will do a fine job.
So, here we go! We’re going to name this hook useMousePosition. Below is the code for the same:
Here is a bit of explanation for the above code:
- First, it has a useState as mouseCoords we default x and y values of 0. This state stores the x and y coordinates of the mouse.
- Next, we have a handleMouseMovement function. This function is an event handler on mousemove events.
- Then, we have a useEffect that adds an event listener on page load for the event mousemove. We remove this event listener when the component is unmounted from the DOM. We do this operation when we want to track the mouse movement across the entire page. This is why we attached the event listener to the window object. To track the mouse position on the entire page, we use a toggle variable called global. If it's true, then we track all of the mouse movement.
- If global is set to false, then the code inside the useEffect is not used and the mouse position tracker completely relies on handleMouseMovement.
- In such a case, while consuming this hook we can pass on the handleMouseMovement function to the target element’s onMouseMove property to track the cursor position inside that element.
Here is how you will be consuming this hook while targeting mouse tracking on a specific div element:
Custom Hooks to Get CSS Variables
CSS variables are entities that can be created and reused later inside a document, also called custom properties. We denote them with a special notation like --primary-color and we access them using a special function called var().
In large projects, we can repeat styles, colors, or sizes in multiple locations. This can get quite difficult to manage and it can also become infeasible to remember these common values.
So, it’s always better to have a central place where you can store these values and use them later. You can consider this a central repository of style properties. These variables, once created, can be accessed anywhere in the document via CSS’s var() function when you declare them inside the :root pseudo-classs selector.
If we compare this methodology in React, then the CSS variables inside these :root classes would act as a global state.
Now, enough of this intro! Let’s create actual CSS variables. Let’s create two variables --primary-color and --font-size-large.
Since we’re using a CRA app, by default there’s a styles.css file inside the src folder. If not, create the file and paste the following code in it:
Simple usage of these variables would be similar to below:
Let’s do some of the fun part here! Consider a scenario where you need to access these CSS variables in your component and change it based on a certain value of an API.
You will need to do following:
- Create a state variable inside a component
- Use the setProperty function to set the new or existing CSS variable values
- Repeat this logic across multiple components
Repeating this logic everywhere would be a hassle. To manage this better, let’s create a React.JS custom hook named useCSSVariable to do exactly the same thing.
Copy and paste the below code in your utility or CustomHooks.ts file (if one doesn’t exist, then create it):
Ok, there are some new moving parts to this hook, but trust me. The pattern of creating a custom hook remains the same:
- useCSSVariable accepts targetElement as an argument. targetElement is the CSS selector for an element that stores the CSS variables.
- Next, we create two state variables: cssRootStyles and rootElement.
- cssRootStyles is a state variable that stores all of the computed styles of the targetElement via getComputedStyle.
- rootElement is another state variable that will store the targetElement. We will use this state to set the CSS variable using setProperty function.
- Then, we have a useEffect that sets the rootElement and cssRootStyles as a targetElement and computedStyles of the root element.
- We also have getCssVariable, which is a simple getter function that gets the CSS variable’s value with the help of getPropertyValue. We return the same value from this function.
- Next, we have setCssVariable, which takes the CSS variable’s name and value and stores them with the help of the setProperty function.
- Finally, we return these two functions so that we can use the consumer component once it’s mounted.
Here in App.js, we simply call the useCSSVariable hook. We finally make use of the getCssVariable function to fetch the --primary-color and --font-size-large CSS variables.
Finally, we created a simple button that creates a new CSS variable --secondary-color with the value #CE8147 when it’s clicked.
So, we learned about hooks and custom hooks in React. We also learned about how we can create our own custom hooks along with some interesting examples, such as a CSS variables fetcher, a fetch API hook, and a few others.
If you’re looking to wrap your stateful logic and share it across multiple components, then you should definitely give custom hooks a try.
Thanks for reading!