Ultimate React Hooks Guide

Published: (December 20, 2025 at 01:30 PM EST)
5 min read
Source: Dev.to

Source: Dev.to

Introduction

React Hooks are functions that let you “hook into” React state and lifecycle features from function components. They were introduced in React 16.8. Below is a list of common React hooks, their explanations, and examples of usage. Additionally, this guide covers how to create custom hooks.

1. useState

The useState hook lets you add state to function components.

Explanation

useState returns an array with two elements: the current state value and a function to update it.

Usage

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    
      
You clicked {count} times

       setCount(count + 1)}>
        Click me
      
    
  );
}

2. useEffect

The useEffect hook lets you perform side effects in function components.

useEffect With Dependencies

useEffect runs after the first render and after every update. You can also specify dependencies to control when the effect runs.

Usage

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  return (
    
      
You clicked {count} times

       setCount(count + 1)}>
        Click me
      
    
  );
}

useEffect With an Empty Dependency Array []

Runs only once after the initial render (similar to componentDidMount).

useEffect(() => {
  console.log('This runs only once after the first render');
}, []);

Use case: Fetching data when the component mounts, setting up event listeners, etc.

useEffect Without Dependencies

Runs on every render (initial and subsequent re‑renders).

useEffect(() => {
  console.log('This runs after every render');
});

Use case: Running code that doesn’t depend on specific state changes, like logging or animations.

Cleanup Function in useEffect

If useEffect returns a function, it acts as a cleanup that runs before the next effect or when the component unmounts.

useEffect(() => {
  const interval = setInterval(() => {
    console.log('Interval running...');
  }, 1000);

  return () => {
    clearInterval(interval); // Cleanup on unmount
  };
}, []);

Use case: Cleaning up event listeners, canceling API calls, clearing intervals, etc.

3. useContext

The useContext hook lets you access the value of a context.

Explanation

useContext accepts a context object (the value returned from React.createContext) and returns the current context value.

Usage

import React, { useContext } from 'react';

const MyContext = React.createContext();

function MyComponent() {
  const value = useContext(MyContext);
  return {value};
}

function App() {
  return (
    
      
    
  );
}

4. useReducer

The useReducer hook is an alternative to useState for managing complex state logic.

Explanation

useReducer returns an array with the current state and a dispatch function to trigger state updates.

Usage

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 };
    default:
      throw new Error('Unknown action');
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    
      
Count: {state.count}

       dispatch({ type: 'increment' })}>
        Increment
      
       dispatch({ type: 'decrement' })}>
        Decrement
      
    
  );
}

5. useCallback

The useCallback hook returns a memoized callback.

Explanation

useCallback returns a memoized version of the callback that only changes if one of the dependencies has changed.

Usage

import React, { useState, useCallback } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, [count]);

  return (
    
      
You clicked {count} times

      Click me
    
  );
}

6. useMemo

The useMemo hook returns a memoized value.

Explanation

useMemo only recomputes the memoized value when one of its dependencies changes.

Usage

import React, { useState, useMemo } from 'react';

function ExpensiveComponent({ value }) {
  // Simulate an expensive calculation
  const computed = useMemo(() => {
    let result = 0;
    for (let i = 0; i Result: {computed};
}

Feel free to extend this guide with additional hooks such as useRef, useLayoutEffect, or custom hooks tailored to your project’s needs.

6. useMemo (Additional Example)

function ExpensiveCalculationComponent() {
  const [count, setCount] = useState(0);
  const [value, setValue] = useState("");

  const expensiveCalculation = useMemo(() => {
    // Assume this is an expensive calculation
    return count * 2;
  }, [count]);

  return (
    
      
Expensive Calculation: {expensiveCalculation}

       setCount(count + 1)}>Increment
       setValue(e.target.value)} />
    
  );
}

7. useRef

The useRef hook returns a mutable ref object.

Explanation

useRef is useful for accessing DOM elements or persisting values across renders.

Usage

import React, { useRef } from "react";

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };

  return (
    
      
      Focus the input
    
  );
}

8. useImperativeHandle

The useImperativeHandle hook customizes the instance value that is exposed when using ref.

Explanation

useImperativeHandle should be used with React.forwardRef.

Usage

import React, { useRef, useImperativeHandle, forwardRef } from "react";

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
  }));

  return ;
});

function Parent() {
  const inputRef = useRef();

  return (
    
      
       inputRef.current.focus()}>Focus Input
    
  );
}

9. useLayoutEffect

The useLayoutEffect hook runs after all DOM mutations.

Explanation

useLayoutEffect is similar to useEffect, but it fires synchronously after all DOM mutations.

Usage

import React, { useLayoutEffect, useRef } from "react";

function LayoutEffectComponent() {
  const divRef = useRef();

  useLayoutEffect(() => {
    console.log(divRef.current.getBoundingClientRect());
  });

  return Hello, world!;
}

10. useDebugValue

The useDebugValue hook is used to display a label for custom hooks in React DevTools.

Explanation

useDebugValue can be helpful for debugging custom hooks.

Usage

import React, { useState, useDebugValue } from "react";

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useDebugValue(isOnline ? "Online" : "Offline");

  // Imagine this is an actual implementation
  return isOnline;
}

11. Custom Hooks

Custom hooks let you extract component logic into reusable functions.

Explanation

Custom hooks are normal JavaScript functions that can call other hooks.

Example: useFetch

import { useState, useEffect } from "react";

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then((response) => response.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}

export default useFetch;

Usage of the Custom Hook

import React from "react";
import useFetch from "./useFetch";

function App() {
  const { data, loading } = useFetch("https://api.example.com/data");

  if (loading) {
    return Loading...;
  }

  return (
    
      {JSON.stringify(data, null, 2)}
    
  );
}

export default App;

This guide provides a comprehensive overview of React hooks, their usage, and how to create custom hooks.

Back to Blog

Related posts

Read more »