How to Use Hooks in ReactJS with Examples
React.js is a powerful library for building dynamic user interfaces with reusable components. Initially, it offered two main types of components: functional and class-based. Though functional components provided a neat and straightforward syntax, they were less capable than class components, particularly when dealing with state or lifecycle events.
To fill this gap, React 16.8 introduced Hooks, a new feature that added powerful functionality such as state management and side effects to functional components.
What are React hooks?
React Hooks are a powerful feature introduced in React 16.8 that allow developers to use state, side effects, and other React features inside functional components. In most cases, they eliminate the need for class components, making it easier to write clean, reusable, and maintainable code. With hooks, functional components can handle complex logic that was once only possible with classes.
Here are some benefits of React hooks:
Simplified codebase: Hooks reduce boilerplate by removing the need for class syntax,
this
bindings, and constructor functions.Better logic reuse: Custom hooks let you extract and reuse stateful logic across components without duplication.
Improved readability: Functional components with hooks are generally easier to read and follow, especially in smaller teams.
Co-located logic: Hooks keep related logic together. For example, fetching data and updating state can live side by side.
Easier testing: Hook-based logic is more modular and testable, especially when broken into smaller custom hooks.
React offers various built-in hooks to help manage component state, side effects, and context more effectively. Let’s explore the most used ones and see how they simplify React development.
Learn React: Hooks
Leverage hooks, a powerful feature of function components, to use states without creating classes.Try it for freeCommonly used ReactJS hooks
React includes a variety of hooks designed to handle different aspects of component logic. Some focus on managing state, others deal with side effects, while a few provide ways to access context or references. Let us understand some essential hooks you’ll encounter and use frequently in React projects.
How to use the useState
hook in React
Unlike class components, functional components don’t have built-in state management. React’s useState
hook fills this gap by allowing you to easily add and manage local state inside functional components.
The useState
hook initializes the state in a functional component. It returns an array with two elements: the current state value and a function to update that state.
Syntax of the useState
hook is:
const [state, setState] = useState(initialValue);
Parameters:
initialValue
: The initial value of the state, which can be any data type (number, string, object, array, etc.).state
: The current state value at any render.setState
: The function used to update the state.
Example:
Here’s a basic counter example that shows how to initialize state and update it on a button click:
import React, { useState } from 'react';function Counter() {const [count, setCount] = useState(0);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment</button></div>);}
In this code:
useState(0)
initializes the state variablecount
with a value of0
.count
holds the current state value, which React keeps track of across re-renders.setCount
is a function provided byuseState
to update thecount
value. WhensetCount
is called, React updates the state and re-renders the component with the new value.The button has an
onClick
event that callssetCount(count + 1)
, which increments the counter each time it’s clicked.React re-renders the
<p>
element with the updated count on every click.
When to use the useState
hook?
Use the useState
hook whenever your component needs to keep track of data that can change over time and affect what’s rendered. It’s ideal for local state that belongs only to that component.
Common scenarios include:
Form fields: Storing and updating user input values
UI toggles: Showing or hiding elements like modals, dropdowns, or tabs
Counters or timers: Tracking clicks, elapsed time, or other incrementing values
Simple user interactions: Managing likes, selections, or toggled settings
If your state is local to a component and doesn’t need to be shared globally or across multiple components, useState
is a clean and effective choice.
But what if your component needs to run code when it renders, fetch data, or responds to changes in state or props? That’s where the useEffect
hook can be used.
Handling side effects in React using useEffect
hook
Functional components don’t have lifecycle methods like componentDidMount
or componentDidUpdate
. That’s where useEffect
comes in, to run the code when a component renders, updates, or unmounts.
The useEffect
hook allows you to handle side effects in your component like fetching data, updating the DOM, or setting up event listeners. It effectively replaces class-based lifecycle methods, bringing that logic into functional components.
How does the useEffect
hook work?
useEffect
accepts a function that runs after the component renders.This function can return another function, which React will call when the component unmounts—great for cleanup.
A second argument, the dependency array, controls when the effect runs.
Example:
Here is a simple example that fetches user data from an API when the component mounts using the useEffect
hook:
import { useEffect, useState } from "react";function UserComponent() {const [user, setUser] = useState(null);useEffect(() => {fetch("https://api.example.com/user/1").then((res) => res.json()).then((data) => setUser(data));// Cleanup function (optional)return () => {console.log("Component unmounted");};}, []); // Runs only once on mountreturn <div>{user ? user.name : "Loading..."}</div>;}
In this code:
useEffect(() => {...}, [])
: Runs once when the component mounts, just likecomponentDidMount
.The empty array
[]
ensures it doesn’t run on updates.The returned function logs when the component unmounts, which is helpful for cleanup like removing event listeners or aborting fetches.
When to use the useEffect
hook
Use the useEffect
hook whenever your component needs to perform side effects—operations that go beyond rendering UI. These include fetching data, subscribing to services, manually updating the DOM, or setting timers.
Typical use cases include:
Fetching or updating data when the component mounts
Setting up and cleaning up event listeners or subscriptions
Triggering animations or external library functions
Syncing local state with external systems (e.g., localStorage, APIs)
If your logic previously lived in lifecycle methods like componentDidMount
, componentDidUpdate
, or componentWillUnmount
, useEffect
is your go-to hook in functional components.
Now, let’s say you need to hold on to a value without triggering a re-render. That’s where the useRef
hook comes in.
What is the useRef
hook in React
The useRef
hook is useful when you need a way to persist values across renders without triggering re-renders. It returns a mutable object with a .current
property that you can update freely. React won’t re-render when it changes. useRef
is commonly used for two primary purposes:
Accessing and manipulating DOM elements directly
Holding on to mutable values (like timers or previous values) without causing re-renders
Syntax of the useRef
hook is:
import { useRef } from 'react';
function MyComponent() {
const myRef = useRef(null);
return <input ref={myRef} />;
}
Here:
useRef(null)
initializes theref
withnull
.The
ref
is attached to an element using theref
attribute.myRef.current
will point to the DOM element after render.
Example:
Let’s see a common use case where clicking a button focuses an input field:
import { useRef } from 'react';function FocusInput() {const inputRef = useRef(null);function handleClick() {inputRef.current.focus();}return (<div><input ref={inputRef} type="text" /><button onClick={handleClick}>Focus the input</button></div>);}
In this code:
We create a
ref
calledinputRef
and attach it to the<input>
element.On button click,
inputRef.current.focus()
is called to focus the input.No re-renders happen when the ref changes.
When to use the useRef
hook
Use the useRef
hook when you need to:
Access or interact with DOM elements directly, such as focusing an input or measuring size
Store mutable values (like timers, interval IDs, or counters) that should persist between renders without triggering re-renders
Keep track of previous values (like tracking the last state or prop)
In short, reach for useRef
when you need a persistent reference that doesn’t affect your component’s rendering logic.
As your application grows, sharing data between many components can get tricky. That’s where useContext
helps by making it easier to pass data through the component tree.
Managing global state with useContext
hook
Passing props through multiple layers of components can become messy and hard to manage. The useContext
Hook solves this problem by allowing components to access shared data without manually passing it down each level.
Unlike useState
, which handles local component state, useContext
is ideal for values needed in many places, like themes, user authentication, or app settings. Let us check how to use the useContext
hook:
Step 1: Create a context using createContext()
To begin, you create a context using React.createContext()
:
const ThemeContext = React.createContext();
This creates a context object to share values across your component tree.
Step 2: Providing context
Use the context’s Provider
component to make data available to any child component:
<ThemeContext.Provider value="dark"><App /></ThemeContext.Provider>
Now, any component inside <App />
can access the value "dark"
using useContext
.
Step 3: Consuming context with useContext
Inside any component that needs the shared value, use the useContext
Hook:
const theme = useContext(ThemeContext);
This will return "dark"
as provided earlier.
Example: Theme switcher
Let’s look at a basic example of switching themes using useContext
:
import React, { createContext, useContext, useState } from "react";// Create contextconst ThemeContext = createContext();// Context provider componentfunction ThemeProvider({ children }) {const [theme, setTheme] = useState("light");const toggleTheme = () =>setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));return (<ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>);}// Consuming componentfunction ThemeButton() {const { theme, toggleTheme } = useContext(ThemeContext);return (<button onClick={toggleTheme}>Current Theme: {theme}</button>);}// App componentfunction App() {return (<ThemeProvider><ThemeButton /></ThemeProvider>);}
In this code:
ThemeContext
holds the shared theme data.ThemeProvider
wraps the app and manages the theme state.ThemeButton
usesuseContext
to access and toggle the theme.The entire tree inside
ThemeProvider
has access to the same theme value.
When to use the useContext
hook
Use the useContext
hook when you need to share data across many components without passing props manually through each level. It’s especially helpful for app-wide settings like themes, user authentication, language preferences, or any state that multiple components need to access.
Avoid using useContext
for frequently changing or deeply nested local state—it’s better suited for global or semi-global data that remains relatively stable.
Now that we’ve seen how to share global state, let’s look at useReducer
, a hook ideal for managing more complex state logic.
Simplifying complex state logic with useReducer
hook
Handling state transitions with multiple conditions or nested values can quickly become tricky. Instead of juggling several useState
calls, useReducer
offers a cleaner, more predictable way to manage state, especially when the logic mirrors a state machine or when actions need to be clearly defined.
Syntax of the useReducer
hook is:
const [state, dispatch] = useReducer(reducerFunction, initialState);
Parameters:
reducerFunction
: A function that takes the currentstate
and anaction
, and returns a new state.initialState
: The starting value for the state, typically an object or a number.
Return value:
Returns an array: [state, dispatch]
, where state
holds the current value and dispatch
triggers state updates.
Example: Managing a counter with multiple actions
import React, { useReducer } from "react";const initialState = { count: 0 };function reducer(state, action) {switch (action.type) {case "increment":return { count: state.count + 1 };case "decrement":return { count: state.count - 1 };case "reset":return initialState;default:return state;}}function Counter() {const [state, dispatch] = useReducer(reducer, initialState);return (<div><p>Count: {state.count}</p><button onClick={() => dispatch({ type: "increment" })}>+</button><button onClick={() => dispatch({ type: "decrement" })}>-</button><button onClick={() => dispatch({ type: "reset" })}>Reset</button></div>);}
Here:
We define an initial state with a
count
of 0.The
reducer
function handles differentaction.type
values to update the state.dispatch()
sends actions to the reducer.The UI buttons call
dispatch()
with respective action types to trigger changes.
When to use the useReducer
hook
Use useReducer
when:
You have complex state logic that involves multiple sub-values or conditions
Updating state with
useState
becomes hard to track or maintainYou need a more centralized and testable approach to handling state transitions
While built-in hooks handle many cases, there are custom hooks that let you extract and reuse complex logic across components without duplication. Let us explore them.
Creating custom hooks in React
Custom hooks let you encapsulate and reuse stateful logic across multiple components, making your code more modular and maintainable. They’re just regular JavaScript functions that use one or more hooks internally and follow the naming convention of starting with use
.
Custom hooks help avoid repetition and keep your components cleaner by moving logic into separate reusable functions.
How to write your own hook in React
A custom hook is a function that:
Starts with the
use
prefix (e.g.,useLocalStorage
)Can call other hooks like
useState
,useEffect
, oruseRef
Accepts parameters to make it reusable
Returns values or functions for components to use
Example: A useLocalStorage
hook
Let’s create a simple custom hook to store and retrieve values from localStorage
:
import { useState, useEffect } from "react";function useLocalStorage(key, initialValue) {const [storedValue, setStoredValue] = useState(() => {const item = window.localStorage.getItem(key);return item ? JSON.parse(item) : initialValue;});useEffect(() => {window.localStorage.setItem(key, JSON.stringify(storedValue));}, [key, storedValue]);return [storedValue, setStoredValue];}
Here:
useState
initializes the value either fromlocalStorage
(if available) or from a default.useEffect
updateslocalStorage
whenever the stored value changes.The hook returns the value and a setter, just like
useState
.
Now you can use it like this in your component:
const [theme, setTheme] = useLocalStorage("theme", "light");
This hook abstracts the localStorage
logic, making the component cleaner and more focused on UI.
When to use custom hooks
Use custom hooks when you find yourself repeating the same logic in multiple components, such as handling forms, timers, or window dimensions. They’re perfect for isolating logic and improving readability without relying on class components or third-party libraries.
Best practices for using React hooks
To write clean, efficient, and maintainable React code, keep these best practices in mind when using hooks:
Call Hooks at the Top Level: Always use hooks at the top of your components or custom hooks—not inside loops, conditions, or nested functions.
Use Descriptive Custom Hook Names: Start with
use
and clearly describe what the hook does, likeuseAuth
oruseLocalStorage
, to improve readability and reuse.Manage
useEffect
Dependencies Carefully: Include all necessary variables in the dependency array to avoid stale data or infinite loops.Clean Up Side Effects: When using
useEffect
, return a cleanup function to prevent memory leaks—especially with event listeners or timers.Never Mutate State Directly: Always use the setter function returned by
useState
oruseReducer
to update state without modifying the original value.
When not to use hooks
When a feature can be achieved using plain JavaScript or a one-off function, don’t force a hook.
In simple components with no state or side effects.
For logic that should be kept global (e.g., Redux or context logic) instead of component-specific.
Hooks are powerful but not always the right tool for every scenario. Use them thoughtfully to keep your components clean, focused, and efficient.
Conclusion
In this article, we explored the core concepts of React hooks, including useState
, useEffect
, useContext
, useRef
, useReducer
, and custom hooks. These hooks allow functional components to manage state, handle side effects, interact with the DOM, and organize logic more effectively, making your React code cleaner and more efficient.
To continue building your React skills and get hands-on practice with hooks, check out Codecademy’s Learn React course. It’s a great way to apply your knowledge and advance your front-end development journey.
Frequently asked questions
1. When should I use useEffect
vs. useLayoutEffect
?
Use
useEffect
for side effects that don’t need to block the browser paint, like fetching data or setting up subscriptions. It runs after the component is rendered and the DOM is painted.Use
useLayoutEffect
when you need to read the layout from the DOM and synchronously re-render before the browser paints. It’s useful for measuring elements or synchronizing animations but use it sparingly as it can affect performance.
2. Why does useEffect
keep running in a loop?
This usually happens when you forget to include a dependency array or incorrectly place changing variables inside it. When no dependency array is provided, useEffect
runs after every render. The effect will keep triggering if the dependencies constantly change (e.g., a state or prop). Make sure to review the dependency list carefully to avoid infinite loops.
3. Can I conditionally call Hooks in my components?
No. Hooks must be called unconditionally and in the same order on every render. Conditional calls can break the Rules of Hooks and lead to unpredictable behavior. Instead, use conditions inside the hook’s body, not around the hook call itself.
4. What are the rules of Hooks, and what happens if I break them?
The two main rules of Hooks are:
- Only call Hooks at the top level (not inside loops, conditions, or nested functions).
- Only call Hooks from React function components or custom hooks.
Breaking these rules can result in bugs and inconsistent state behavior. React provides a linting plugin to help you catch violations.
5. Can I use custom hooks in class components?
No. Hooks—including custom hooks, only work with function components.
'The Codecademy Team, composed of experienced educators and tech experts, is dedicated to making tech skills accessible to all. We empower learners worldwide with expert-reviewed content that develops and enhances the technical skills needed to advance and succeed in their careers.'
Meet the full teamRelated articles
- Article
Building a Redux Store from Scratch Using Vanilla JavaScript
Learn how to create a Redux store from scratch using vanilla JavaScript, with a step-by-step guide covering state management, listeners, and dispatching actions. - Article
How to use React Developer Tools
Learn how to install, inspect components, and modify React elements using React Developer Tools. Explore Chrome DevTools to debug your react applications effectively. - Article
How to Implement MVC Architecture in a Full-Stack App
Learn how to implement MVC architecture in a full-stack expense tracker app using JavaScript, Node.js, Express, React, and PostgreSQL.
Learn more on Codecademy
- Free course
Learn React: Hooks
Leverage hooks, a powerful feature of function components, to use states without creating classes.Beginner Friendly5 hours - Free course
Learn React
In this React course, you’ll build powerful interactive applications with one of the most popular JavaScript libraries.Intermediate13 hours - Course
Learn Advanced React
Learn advanced React techniques and topics including custom hooks, error boundaries, the Context API, and optimization.With CertificateAdvanced5 hours