React hooks were first introduced in React 16.8.0 and have significantly changed the way developers write code in React. Hooks allow you to utilize state and other React features without the need for class components.
This article provides a comprehensive overview of React hooks, covering how to use them, their significance, and best practices for implementing them in your projects. Additionally, we'll guide you through creating your custom React hook from scratch, equipping you to leverage their capabilities in your apps immediately.
Whether you're a seasoned developer or just starting with React, this article will equip you with all the necessary knowledge required to harness the complete potential of React hooks in your projects.
What Are React Hooks?
In early React versions, lifecycle methods were exclusive to class components. Hooks offer a simplified and more intuitive approach to handle state and lifecycle events in functional components, which were initially prescriptive to presentational and stateless functions.
What Are the Key Advantages of Using React Hooks?
When hooks were first introduced in React, the React team sought to address a few key challenges with class components.
Firstly, the team recognized that maintaining components that grow into an unmanageable mess of stateful logic and side effects makes it easy to introduce bugs and inconsistencies. They introduced hooks to help developers avoid this pitfall by isolating stateful logic in separate functions.
Secondly, the React team acknowledged that classes can be a significant learning barrier to React. Developing within classes can be frustrating for developers accustomed to other programming languages who struggle to grasp how the
this keyword works in React. Moreover, class components can lead to unintentional patterns that slow down code compilation in React.
React hooks provide a solution to these challenges by providing developers with additional features and facilitating the creation of more modular and reusable components. This makes it easier to write and maintain complex applications while avoiding the issues that can arise from using class components.
How to Use React Hooks?
It is critical to keep in mind that every React hook begins with the word
use, a crucial rule to consider when creating custom hooks. Keep in mind that hooks can only work in React function components and not in class components.
React provides a wide range of built-in hooks, such as
useReducer, and many others.
To illustrate, consider this simple example that uses the
useState hook in a function component to create a counter.
Examples of React Hooks
As previously stated, React includes several default hooks in the version 16.8.0 release.
In this section, we'll delve into the essential and useful hooks you need to be aware of.
useState hook is one of the state hooks in React and also one of the most commonly used. It allows components to have access to and manipulate stateful logic in an application. In React, a state is any information in your application that changes over time, usually triggered by user interaction.
useState hook has the following signature:
const [state, setState] = useState(initialState);
useState hook is a function that takes an initial state (
initialState) as its only parameter and then returns an array with two items: the current state value (
state) and a function to update that value (
Here’s an example showing how you’d use the
useState hook to create a popup that appears on the screen when the user clicks a button:
Here’s an explanation of what is going on in the code above.
To use any of the built-in hooks in React, we have to first import it from the
useState hooks work by returning an array with two values: the current state value and a function to update that value.
In the above example, we defined the function as follows:
const [isOpen, setIsOpen] = useState(false);
The above code snippet defines a
useState function holding a boolean as its initial value. It returns two items,
isOpen, which holds the current state value, and
setIsOpen, which is a function to update the state value.
Next, in the code, we created a function
togglePopup that runs the
setIsOpen function, which changes the current state value whenever a button is clicked and causes React’s virtual DOM to re-render.
We used the
useState hook in this example to manage the state of the popup, which changes after the user performs an action (clicking a button).
You can go through the React docs here to learn more about the
useEffect hook is a built-in hook in React that allows components to synchronize with external systems in a functional way. External systems (also called side effects) include browser APIs, third-party widgets, networks, and so on.
useEffect hook can be declared as follows:
It’s a function that takes two parameters:
setup, which is a function, and an optional array of
setup function contains your Effect’s logic and is executed after the component is mounted or updated. The optional
dependencies are an array containing all reactive values referenced inside of the
dependencies array is used to control when the effect should be re-run. If any of the values in the array changes, the component where the hook is used will re-render, and the
useEffect function will re-run. If you don’t add the
dependencies array to the hook, your Effect function will re-run after every re-render of the component where it's used.
Additionally, if you specify an empty array for
dependencies, the Effect function will run only once at the initial render but never in subsequent re-renders.
Here’s an example showing how to use the
useEffect hook to fetch data from an external API:
In the above example, we’re running a function
fetchData in the
useEffect hook to fetch data from a remote API somewhere (a good example of an external system). Then we store the data from the API in a
data variable using the
useState hook. We also passed an empty dependency array to
useEffect, which means the hook will only run once when the component is first mounted.
useReducer hook is another built-in hook for managing stateful logic in your React applications. It’s often used as an alternative to
useState to manage a complex state or when the next state depends on the previous one.
useReducer hooks manage the state in your React application by letting you use a reducer (a function that takes in the current state and an action object and returns a new state based on the action) in your components.
The signature for the
useReducer hook is as follows:
const [state, dispatch] = useReducer(reducer, initialArg, init?)
useReducer hook takes two required parameters, reducer and initialArg, and one optional parameter, init. The reducer is a function that specifies how the state gets updated. initialArg is the value from which the initial state is calculated, while init is used as an initializer function to create the initial state if it’s not already created.
useReducer returns two values:
state, which is the current state, and
dispatch, a function for updating the state to a different value (also causing a re-render).
Below is an example showing the
useReducer how can we use the
useReducer hook to manage a list of users in an array, with the functionality to create and delete a user.
In the example above, the
initialState represents the initial state of the user's array, which contains a single user. The
userReducer is a function that takes the current state and an action and returns the updated state based on the action type.
Next, we use the
handleDeleteUserfunctions as event handlers to dispatch actions to the reducer to update the state. The
UserList component then renders the list of all users, each with delete functionality, and finally, a form to add new users to the list.
useContext is one of the built-in hooks in React that allow you to interact with your application state. Specifically,
useContext allows you to read and subscribe to the Context API to pass data around in your components.
If you’re familiar with React, then you already know data can only be passed in a unidirectional way - from the parent component to child components via props.
However, when you use this pattern for passing data from a parent component through multiple nested child components to a deeply nested child component that actually needs the data, it won’t be long before you find yourself in a terrible situation known as “props drilling.”
The major problem the Context API and
useContext solve is to provide an alternative way for passing data through multiple and deep levels in the component tree without letting you go through props drilling.
useContext hook provides a way to interact with the Context API to make the data available in any part of your application.
The signature for the
useContext hook is as follows:
const value = useContext(SomeContext)
useContext accepts only one parameter:
SomeContext. Before the
useContext hook can be used, you'd need to have created the
SomeContext object somewhere higher in the component tree. This context object represents the kind of information you can provide or read from components that need the data.
This great example below from the React docs shows how you can use the
useContext hook to pass theme data between the main parent component
App.js, to child components,
Panel, without using props.
To use the Context API in React, we first need to initialize the context with a value using the
createContext function from React.
In the above example, the
MyApp component is the higher-level context provider that creates the
ThemeContext context and makes it available to its child components using a
</ThemeContext.Provider> component. The child components (
Button) that need access to the data provided by the context provider can then use the
useContext hook from React to access the data directly without having to pass it through props.
By using the
useContext hook with the Context API to pass data around in a React application, we can avoid unnecessary props drilling in the component tree and write more efficient and intuitive React code.
useCallback hook is one of React’s built-in hooks for improving your application performance. Its function is to cache expensive function calls between re-renders.
Its signature looks like so:
const cachedFn = useCallback(fn, dependencies)
The two parameters for
fn: The function you want to cache between re-renders
dependencies: An array of all the reactive that is referenced in
The return value of
useCallback (cachedFn in this case) is a cached version of
fn that only changes if one of the dependencies has changed.
The following example from the React docs shows how you can use the
useCallback hook to cache a function and prevent the component where it’s used from re-rendering.
In the above example, we’re passing a
handleSubmit function down from the
ShippingForm component. Remember, in React, when a component re-renders, React re-renders all of its children recursively. This means when
ProductPagere-renders with a different
ShippingForm component also re-renders.
So let’s say, for some reason, the
ShippingForm component is very slow to render. If the
ShippingForm is being re-rendered every time the
ProductPage re-renders, this can hurt the performance of our app significantly.
To avoid slowing down our app, we wrapped the
handleSubmit function in a
useCallback hook. This ensures that the function is the same between the re-renders until any of the dependencies change. More importantly, it ensures the
ShippingForm component skips re-rendering.
You’ll often find
useMemo used in improving performance in React apps. However, there’s an important difference between the two optimization hooks that you should take note of.
The difference is in what each hook caches exactly.
useCallback will not call the function you provided as an argument, but catches the function itself.
useMemo, on the other hand, will run the function you passed as an argument and cache its result instead.
useId hook is also a built-in React hook and is used for generating unique IDs on the client-side and server side.
Its signature is as follows:
const generateID = useId()
useId hook accepts no parameters but returns a unique string every time it’s called.
A good use case for the
useId hook is generating unique IDs for HTML accessibility attributes. It’s useful for connecting two HTML elements to each other.
Take for example the following input element, and a label element with an
htmlFor attribute describing the value of the input element.
So, instead of hardcoding the I'd value of the input, which is considered a bad practice in React, we used the
useId hook to generate a unique ID that can be referenced by both the input and label elements.
You might be tempted to use the
useId hook to generate keys when rendering list items in your React app. This is bad practice and it also poses some serious performance pitfalls for your app.
If you need to generate unique keys for a list, consider using a library such as
useLayoutEffect is a built-in hook in React that works similarly to the
useLayoutEffect is commonly used as an alternative to
useLayoutEffect differs in how it executes its side effect code - it runs synchronously immediately after React has performed all DOM mutations.
Its signature is also similar to
useLayoutEffect hook accepts two parameters;
setup, which contains your Effect logic, and an optional
dependencies array. Its return value is
The most common use case for
useLayoutEffect is measuring the layout of a page. For example, getting the height and width of a DOM element.
Let’s take a look at an example.
In the example above, we're using
useLayoutEffect to measure the width and height of a DOM element (a div in this case) using the
getBoundingClientRect method, and updating the state with the measured values. We also passed an empty dependencies array as the second argument to
useLayoutEffect. This means the effect will only run once, after the initial render.
It's important to note that because
useLayoutEffect can block the browser's layout and paint process, you should use it sparingly and only when necessary. And in most cases, you’ll find
useEffect sufficient for performing side effects in functional components.
useMemo hook is another one of React’s built-in hooks for improving performance in your application. It works by letting you memoize expensive function calls so that you can avoid calling them on every render.
Wikipedia defines memoization as follows:
Memoization or 'memoisation' is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
useMemo hook can be used as follows:
const cachedValue = useMemo(calculateValue, dependencies)
As you can see above, the hooks accepts two parameters;
dependencies. It returns a single value, which is the cached result from the expensive function.
calculateValue is the expensive function which you want to cache. This function must be pure, have no argument and can return any type of value.
calculateValue will be run during the initial component render. During subsequent re-renders of the component, React will return the same value again if
dependencies hasn’t changed, otherwise, it’ll run the
calculateValue function again, return its return and store it.
dependencies contains the list of changing values that are used in the
calculateValue function. The changing values could be state, props, or variables defined in your component.
Let’s look at an example. Imagine you have a function in your component that sorts about 1000 blog posts before displaying them in a list. Running this type of function during every re-render of your component is bad and and will significantly lower the performance of your app.
Here’s how we can fix it with
useMemo in the above example, we memoize the function for sorting the array of blog posts and only recalculate it when the
postsprop changes. This avoids unnecessary re-renders of the component and improves your app performance.
The basic example above shows how we can optimize our React applications for performance without going through too much hassle. However, beware of using the
useMemo hook at every opportunity to optimize your application.
If you find out your app is slow, try to figure out the cause and solve the problem before using
useMemo for optimization.
useRef hook is a built-in React hook that allows you to create a mutable reference to an element or value that does not update across renders. In other words, it allows you to reference a value that’s not needed for rendering.
Aside from the above use case, it is also the most go-to hook for accessing and manipulating the DOM in React.
Its signature is as follows:
const ref = useRef(initialValue)
useRef accepts only one parameter:
initialValue: This is value you want the ref object’s
currentproperty to be initially. It can be a value of any type, such as a string, boolean, integer, and so on.
It returns an object with a single property,
curent's value is initially set to the
initialValue you passed to
useRef, but you can change it to something else.
The example below shows how you can use the hook to automatically focus an input element after the click of a button:
In the example above, we created a reference to the input element using
useRefand assign it to the input element using the
ref prop. We then use
inputRef.currentto access the input element’s DOM node directly in our
handleClick function and call its
focus method to bring the input into focus.
useTransition is a built-in React hook for optimizing your app’s UI performance. This hook is useful if you’re transitioning your screen to render a component that makes expensive state updates. For example, trying to render a list containing hundred of items.
While there are popular solutions for deferring heavy state updates that could make your app unresponsive, such as using
Suspense or loading spinners,
useTransition skips any of these processes and make sure we have some content available on the next screen before we transition to it.
useTransition slows down page transition to ensure data or content is available on the next screen (which is great for your users’ experience).
The signature for the
useTransition hook is as follows:
const [isPending, startTransition] = useTransition()
useTransition hook doesn’t take any parameters, but returns these two values:
isPending: a boolean value used by React to tell us whether there’s an ongoing transition.
startTransition: a callback function for marking slow state updates as transitions.
Let’s go through an example to better understand how the hook works.
In the example above, we’re using the
useStatehook to create two state variables,
list is initialized as an empty array, and will be updated with the list of items fetched from the API.
isPending is a boolean flag that is used to indicate whether the transition is in progress.
ListTabButton component is a reusable button component that accepts an
onClick callback function and
children as props. The
useTransition hook is used to create a
startTransition function, which is used to initiate the transition.
When the button is clicked, the
onClick callback function is executed inside a
startTransition function. This signals to React that a transition is about to start, and React will use this information to optimize the rendering of the component.
How to Build a Custom React Hook?
In this section, we’ll build a custom React hook from scratch. Our hook, which we’ll call
useMediaQuery, is a hook that allows you to detect the current media query that the user's device matches. This can be useful if you want to allow some specific features to work or not on your website, depending on the type of device the user is accessing your site with.
Before we start building out our custom hook, there are some hooks rules in React you should keep in my mind. And if you violate any of these rules, there’s a linter plugin in React you can install that will raise errors or warn you to ensure you adhere to these rules.
Here are the important rules to remember:
Your custom hook name must start with the
You can only call hooks at the top level. Meaning you can’t use hooks in functions, inside another hook, loops, or nested functions. However, you can use React built-in hooks in a custom hook.
Only call hooks from React functions (function components and custom hooks).
Now let’s get back to building our custom hook.
Create a file
useMediaQuery.js with the following content:
Basically, our custom hook,
useMediaQuery, takes a single parameter, which is the media query to match against. The hook then returns true or false, indicating whether the device currently matches the given media query.
Let’s break down the above code even further.
We start by importing the
useEffecthooks from the react library.
useMediaQueryfunction (which is our hook function), we declare a state variable called
useStatehook. This variable will hold the Boolean value indicating whether the device matches the specified media query.
We then use the
useEffecthook to set up a listener for changes in the device's media query. We also pass in the
queryargument as a dependency, this will make the hook to re-run whenever the query changes.
matchesproperty of this object matches the current value of
matchesin our state. If it doesn't, we update the state to reflect the new value.
Finally, we return the
matchesstate variable, which holds the Boolean value indicating whether the device currently matches the specified media query.
And here’s how we’d use our hook in a React component:
This is just to give you an idea of what is achievable with hooks in React. There’s no limit to what logic you can extract into a hook. Ensure you’re not breaking the rules!
You can also check out usehooks.com to see more examples and get ideas of custom React hooks you can use in your next project.
The Bottom Line
Hooks are a powerful addition and game-changers to React. They allow you to isolate and reuse similar functionalities across your apps.
With React Hooks, developers can write intuitive React code and demystify complex applications.
In this article, we walked you through the ins and outs of React Hooks. You learned why Hooks were introduced into React, what problems they solve, and how you can start using them in your projects. And more importantly, you are now a rock star in creating your own custom React hooks.