React Hooks

React Hooks are special functions that allow you to use state and other React features inside functional components. Introduced in React 16.8, they let you "hook into" React's lifecycle and state management without writing a class.
Why Were Hooks Introduced?
Before Hooks, if you needed a component to have its own state (e.g., to track a counter's value) or to perform an action after rendering (e.g., to fetch data), you had to convert it into a class component. Class components involved more boilerplate code, were often harder to read, and the this
keyword could be a common source of confusion.
Hooks were created to solve these problems, allowing developers to use React's powerful features in simpler, cleaner functional components.
Why Hooks Are Useful 🪝
Write State-Driven UIs with Simple Functions: Hooks, primarily
useState
, give functional components the ability to hold and manage their own state. This was the biggest advantage of class components, and Hooks make it available in a much simpler format.Easier to Reuse Logic: Hooks allow you to extract stateful logic from a component so it can be tested independently and reused. You can create your own custom Hooks (e.g.,
useFetch
for data fetching oruseLocalStorage
to interact with browser storage) and share them across multiple components, which is much cleaner than older patterns like render props or higher-order components.More Readable and Less Code: Hooks lead to more organized and compact components. Related logic (like setting up a subscription and cleaning it up) can be kept together inside a single
useEffect
Hook, whereas in class components, this logic was often split between different lifecycle methods (componentDidMount
andcomponentWillUnmount
). This co-location makes the code easier to follow.
Types of Hooks:
useState:
Think of
useState
as a light switch. It can be in one of two states:on
oroff
. Your component needs to remember this state. The hook gives you two things:The current state (
true
for on,false
for off).A function to flip the switch.
When you flip the switch, the room's appearance (your UI) changes.
- Use it for: Remembering simple things that change, like whether a menu is open or closed.
Example: A button to show or hide a message.
import { useState } from 'react';
function SecretMessage() {
// The light switch, starting in the 'off' (false) position.
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? 'Hide' : 'Show'} Message
</button>
{/* If isVisible is true, the message shows up. */}
{isVisible && <p>The secret code is 1234!</p>}
</div>
);
}
useEffect
:Think of
useEffect
as setting an alarm on your phone. You tell it: "When I wake up in the morning (when my component first appears), do this one specific thing."- Use it for: Doing something once right after your component loads, like fetching initial data.
Example: A component that fetches and displays a random dog picture when it first appears.
import { useState, useEffect } from 'react';
function DogPicture() {
const [imageUrl, setImageUrl] = useState('');
// The alarm: When the component first loads...
useEffect(() => {
// ...go get a dog picture from the internet.
fetch('https://dog.ceo/api/breeds/image/random')
.then(res => res.json())
.then(data => {
setImageUrl(data.message);
});
}, []); // The empty [] means "only run this alarm once".
return <img src={imageUrl} alt="A Random Dog" />;
}
useMemo:
Imagine you have a very complex cake recipe that takes hours to prepare. If you need this cake for several different events today, you don't bake a new one from scratch each time. You bake it once and use that same cake all day. You only bake a new one tomorrow.
useMemo
does this with slow calculations. It "pre-bakes" the result and only re-calculates it if the ingredients change.- Use it for: Skipping slow calculations that don't need to be re-done every time the screen updates.
Example: Finding the "most expensive" item in a very long list of products. This calculation only needs to run again if the list of products itself changes.
import { useMemo } from 'react';
function ShoppingList({ products }) {
// Pre-bake the result of this slow calculation.
const mostExpensiveItem = useMemo(() => {
console.log("Finding the most expensive item (slow)...");
return products.sort((a, b) => b.price - a.price)[0];
}, [products]); // Only re-bake if the 'products' list changes.
return <p>The most expensive item is: {mostExpensiveItem.name}</p>;
}
useCallback
:Imagine you write down instructions on a piece of paper for a helper.
If every time you talk to your helper, you throw away the old paper and write the exact same instructions on a new piece of paper, the helper might think they're new and re-read them carefully just in case (this is a component re-rendering).
useCallback
is like writing the instructions once and just pointing to that same piece of paper every time. The helper recognizes the paper and knows they don't need to waste time re-reading it.- Use it for: Giving a child component the exact same function every time, so it doesn't re-render for no reason.
Example: We have a counter and a separate "Reset" button. We don't want the "Reset" button to re-render every time the count increases.
import React, { useState, useCallback } from 'react';
// A simple button that is optimized to not re-render if its props are the same.
const ResetButton = React.memo(function ResetButton({ onReset }) {
console.log("Reset Button is rendering...");
return <button onClick={onReset}>Reset Count</button>;
});
function Counter() {
const [count, setCount] = useState(0);
// We write our "instructions" once and useCallback "saves the paper".
const handleReset = useCallback(() => {
setCount(0);
}, []); // The empty [] means the instructions never change.
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
{/* We pass the same "piece of paper" (handleReset) to the button. */}
<ResetButton onReset={handleReset} />
</div>
);
}
useRef:
Think of
useRef
as a bookmark. You place it on a specific page in a book so you can find that page instantly without flipping through from the start. The bookmark itself doesn't change the book's story (it doesn't cause a re-render). It's just a direct pointer.- Use it for: Getting a direct reference to an HTML element to do things like focus it or measure its size.
Example: A chat app that automatically scrolls down to the newest message. The useRef
is a bookmark placed on the last message.
import { useRef, useEffect } from 'react';
function ChatBox() {
// The bookmark.
const lastMessageRef = useRef(null);
// After rendering, scroll the book to the bookmark.
useEffect(() => {
lastMessageRef.current.scrollIntoView({ behavior: 'smooth' });
});
return (
<div>
{/* ... a long list of messages */}
<div ref={lastMessageRef}>This is the newest message!</div>
</div>
);
}
useTransition
:You're an elevator operator. A very important person (an urgent update, like user typing) arrives and needs to get on now. You immediately pause the process of letting a large, slow-moving crowd on (a non-urgent update, like loading a big list) to let the important person get in first. The ride is smooth for them, and the crowd gets on right after.
- Use it for: Keeping your app responsive by telling React that a slow update can wait a moment if a more important update comes in.
Example: As you type in a search box, your typing is the "important person." The list of search results is the "slow crowd."
import { useState, useTransition } from 'react';
function SearchResults() {
const [isPending, startTransition] = useTransition();
const [query, setQuery] = useState('');
const handleChange = (e) => {
// Let the "important person" (your typing) through immediately.
setQuery(e.target.value);
// Let the "slow crowd" (the search results) wait if needed.
startTransition(() => {
// ...code to update a very long list of results based on the query.
});
};
return <input value={query} onChange={handleChange} />;
}
Subscribe to my newsletter
Read articles from Syed Wasif Hussain directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
