Advanced React Hooks You’re Probably Not Using, But Should!
Advanced React Hooks You’re Probably Not Using, But Should!
Advanced React Hooks You’re Probably Not Using, But Should!

Advanced React Hooks You’re Probably Not Using, But Should!

By:

Ayushi Chauhan

14 Aug 2025

Think you know React Hooks? If your mind immediately jumps to useState and useEffect, you’re only scratching the surface. React offers a treasure of powerful hooks that can boost your performance, simplify complex logic, and make your code cleaner than ever. In this blog, we’ll uncover 9 advanced hooks that go beyond the basics, complete with examples you can start using today.

Hooks

React Hooks

(Img Source)

Hooks are special functions in React that let you use state, lifecycle, and other React features in functional components, without writing class components. They were introduced in React 16.8 and allow you to use features that were previously only available in class components.

We all know about the basic and most used hooks mentioned below:

  • useState: useState hook in React JS is a fundamental hook that allows functional components to manage and update state.

  • useEffect: useEffect is a React Hook that allows functional components to perform side effects (data fetching, subscriptions, DOM updates).

  • useContext: useContext in React JS is a Hook that allows functional components to subscribe to a React Context and read its value. It is part of the React Context API, which provides a way to share data across the component tree without having to manually pass props down through every level of the hierarchy (a problem known as "prop drilling").

  • useRef: useRef hook in React.js is a built-in Hook that provides a way to create a mutable reference to an element or a value that persists across renders without triggering re-renders of the component. 

There are many more useful hooks that can be used based on your requirement.

1. useMemo 

  • It is a React Hook that optimizes performance by memoizing (caching/storing) the result of a computationally expensive function or calculation.

  • It prevents unnecessary re-execution of this function on every component re-render, ensuring it only runs when its dependencies change.  

Instead of using this

  const [multiply, setMultiply] = useState<number>(0); 


  useEffect(() => { 
    setMultiply(num * 5); 
  }, [num]);

Use this

const multiply = useMemo(() => { 
    return num * 5; 
  }, [num]);

As you can see above using useMemo hook we don’t need to create a separete state and useEffect for updating state , it simply returns the value updated by the function on dependency change. 

2. useCallback

  • It is a React Hook that memoizes a function definition between renders.  

  • It is used to optimize performance by preventing unnecessary re-creation of functions, especially when those functions are passed as props to child components or are dependencies in other Hooks like useEffect.

Insted of using this

  const handleClick = () => { 
    setCount((prevCount) => prevCount + 1); 
  };

Use this

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

3. useReducer

  • useReducer Hook in React is an alternative to useState for managing component state.T

  • his hook particularly useful when state logic becomes complex or involves multiple related state values.

  • It uses reducer function.

  • You define a reducer function that takes the current state and an action object as arguments.

  • This function contains the logic for how the state should be updated based on the action dispatched.

  • It returns an array containing:
    1. The current state.
    Here current state is an updated state returned by the function that is paased to useReducer hook
    2. A dispatch function.
    A dispatch function lets you update the state to a different value and trigger a re-render. You need to pass the action as the only argument to the dispatch function.

"use client"; 
import { Box } from "@mui/material"; 
import { useReducer } from "react"; 

function reducer(state, action) { 
  switch (action.type) { 
    case "increment": 
    default: 
      return state; 
  } 
} 

export default function Counter() { 
  const [count, dispatch] = useReducer(reducer, 0); 

  return ( 
    <Box> 
      <button onClick={() => dispatch({ type: "increment" })}> 
        Count: {count} 
      </button> 
    </Box> 
  ); 
}

4. useId 

  • It is a React Hook used for generating unique IDs that can be passed to accessibility attributes within your components . 

  • Ids generated by this hook remain stable across server-side and client-side rendering which is too good for hydration. 

  • Its primary purpose is to provide unique IDs for accessibility attributes like id, htmlFor, aria-labelledby, and aria-describedby.

Instead of using this

 <FormControl> 
        <InputLabel htmlFor="my-input">Email address</InputLabel> 
        <Input id="my-input" aria-describedby="my-helper-text" /> 
        <FormHelperText id="my-helper-text"> 
          We'll never share your email. 
        </FormHelperText> 
 </FormControl>

Use this

  //generate ids  
  const id = useId();  
  const inputId = `${id}-input`; 
  const helperId = `${id}-helper`; 

     //use it 
   <FormControl>  
        <InputLabel htmlFor={inputId}>Email address</InputLabel> 
        <Input id={inputId} aria-describedby={helperId} /> 
        <FormHelperText id={helperId}> 
          We'll never share your email. 
        </FormHelperText> 
   </FormControl>

5. useLayoutEffect

  • This react hook is same as useEffect, the only difference is in its execution timing. 

  • useEffect runs asynchronously after the browser has painted the screen. 

  • useLayoutEffect runs synchronously immediately after all DOM mutations have been applied by React, but before the browser paints the screen. 

Instead of using this

  useEffect(() => { 
    if (boxRef.current) { 
      boxRef.current.style.backgroundColor = "blue"; 
    } 
  }, []);

Use this

  useLayoutEffect(() => { 
    if (boxRef.current) { 
      boxRef.current.style.backgroundColor = "blue"; 
    } 
  }, []);

6. useImperativeHandle 

  • React Hook that allows a child component to expose a limited, custom interface to a parent component when a ref is attached. 

  • We need to use 3 things here :
    1. useRef
    2. forwardRef
    3. useImperativeHandle 

Here

  • useRef is used in parent component that creates a link between parent and child component or components.

  • forwardRef is used in child component and used for receiving ref from parent to childcomponent so that parent component can have the special access to child component’s DOM node or custom functions (via useImperativeHandle). 

  • useImperativeHanle is also used inside child component used for specifying the accessibility of child component’s methods or values that can be used by parent component. 

Page.tsx (parent component)

"use client"; 
import React, { useRef } from "react"; 
import { Button } from "@mui/material"; 
import Child from "@/components/Child"; 

const page = () => { 
  const btnRef = useRef<HTMLInputElement|null>(null); 
  return ( 
    <div> 
      <Child ref={btnRef} /> 
      <Button variant="contained" onClick={() => btnRef.current.hi()}> 
        Click 
      </Button> 
    </div> 
  ); 
}; 

export default page;

Child.tsx (child component)

import React, { forwardRef, useImperativeHandle } from "react"; 
import { Typography } from "@mui/material"; 

function Child(props: any, ref: React.Ref<any>) { 
  useImperativeHandle( 
    ref, 
    () => ({ 
      hi: () => alert("Hello"), 
    }), 
    [] 
  ); 

  return ( 
    <div> 
      <Typography sx={{ color: "black" }}>Child Component</Typography> 
    </div>

7. useActionState

  • The useActionState hook in React is a new addition, primarily designed to streamline the management of state updates based on the results of form actions.

  • It simplifies handling asynchronous operations related to forms, such as submissions, by providing a direct way to manage the state and feedback (like loading or error states).

  • This hook is used for enabling seamless form handling with state management between the server and client.

  • It is designed to streamline state updates based on the result of a form action.

  • It takes two arguments:
    1. initialState: The initial value for the state managed by the hook.
    2. action function: This is the function that will be executed when the form action is triggered (e.g., when a form is submitted). It receives the previousState and formData as arguments.

  • It returns an array containing:
    1. state: The current state value, which is updated based on the return value of the action function.
    2. formAction: A new function that you pass to the formAction attribute of your form element or a button's formAction attribute.
    3. isPending (optional): A boolean indicating whether the action is currently in progress.

"use client"; 
import { useActionState } from "react"; 
import { Box, Button, TextField, Alert } from "@mui/material"; 

export async function submitAction( 
  prevState: { error?: string; success?: string }, 
  formData: FormData 
): Promise<{ error?: string; success?: string }> { 
  const email = formData.get("email")?.toString(); 
  const password = formData.get("password")?.toString(); 

  if (!email || !password) { 
    return { error: "Email and password are required" }; 
  } 
  if (password.length < 6) { 
    return { error: "Password too short" }; 
  } 
  await new Promise((res) => setTimeout(res, 1000)); 
  return { success: "User registered successfully!" }; 
} 
export default function RegisterPage() { 
  const initialState = { 
    error: "", 
    success: "", 
  }; 
 
  const [state, formAction, isPending] = useActionState( 
    submitAction, 
    initialState 
  ); 
 
  return ( 
    <Box 
      component="form" 
      action={formAction} 
      sx={{ 
        maxWidth: 400, 
        mx: "auto", 
        display: "flex", 
        flexDirection: "column", 
        gap: 2, 
        p: 3, 
        border: "1px solid #e0e0e0", 
      }} 
    > 
      {state?.error && <Alert severity="error">{state.error}</Alert>} 
      {state?.success && <Alert severity="success">{state.success}</Alert>} 
      <TextField label="Email" name="email" type="email" required fullWidth/> 
      <TextField 
        label="Password" 
        name="password" 
        type="password" 
        required 
        fullWidth 
      /> 
      <Button 
        type="submit" 
        variant="contained" 
        color="primary" 
        disabled={isPending} 
      > 
        {isPending ? "Registering..." : "Register"} 
      </Button> 
    </Box> 
  ); 
}

As you can see in the above example using this hook we don’t need to to create a separate state for storing and updating data as well as for loading state...we can simply get all input Feilds data in formData and handle those data using the function that is passed to useActionState hook. 

8. useLinkStatus

  • This react hook is used to monitor the status of a link, such as whether it is currently being prefetched, visited, or idle.

  • It returns an object containing a pending boolean property, which is true when the link is in a pending state and false otherwise.

  • In modern single-page apps, a user might click on a link and nothing seems to happen that leads bad user experience.

  • Using this hook we can display some message , loader , spinner and any custom component when there is a blocker while navigating to a link.

  • useLinkStatus is useful in the following situations:
    1. Prefetching is disabled or still in progress: This ensures that there is some delay, allowing your pending state logic to execute and be visible to the user.
    2. The destination route is dynamic and doesn't include a loading.js file: This again ensures there is a delay for the pending state logic. The loading.js file shouldn't be available because it would show up instead of the pending state logic.

"use client"; 
import Link, { useLinkStatus } from "next/link"; 

function MyLinkComponent() { 
  const { pending } = useLinkStatus(); 
 
  return ( 
    <Link href="/actionState"> 
      Click me {pending && <span>(Loading...)</span>} 
    </Link> 
  ); 
} 

export default MyLinkComponent;

9. use

  • React 19 introduces a significant new hook called use()

  • It handle asynchronous operations, particularly data fetching and context consumption. 

  • It allows you to directly read the value of a promise or consume context within a component's render phase.

import { use } from "react"; 

async function fetchData() { 
  const response = await fetch("https://jsonplaceholder.typicode.com/posts"); 
  return response.json(); 
} 

function MyServerComponent() { 
  const data = use(fetchData()); // Directly await the promise 
  return ( 
    <div> 
      <h1>Data:</h1> 
      <pre>{JSON.stringify(data, null, 2)}</pre> 
    </div> 
  ); 
} 

export default MyServerComponent;

As you can see above, we don't need to create a separate state and run useEffect for storing and updating data , we simply created async function that fetch data while component render phase storing it in a variable using this hook and using it directly.

Conclusion

React Hooks have completely transformed the way developers build functional components, moving far beyond just useState and useEffect. By exploring advanced hooks like useMemo, useCallback, useReducer, useId, useLayoutEffect, useImperativeHandle, useActionState, useLinkStatus, and the new use() hook in React 19, you can write cleaner code, improve performance, and handle complex logic with ease. Mastering these React Hooks will not only streamline your development process but also help you create faster, scalable, and more efficient React applications.

Frequently Asked Questions

1. Can you use Hooks in class components?

No, you cannot use React Hooks inside class components. Hooks are designed exclusively for functional components and must follow the rules of hooks (e.g. only call them at the top level). If you're working in class components, you’ll need to convert them to functions to leverage Hooks.

2. What’s the difference between a custom Hook and a regular function?

A custom Hook lets you encapsulate stateful logic using built-in React Hooks, like useState or useEffect, and reuse that logic across components. A plain JavaScript function doesn’t offer access to React lifecycle or state. In short, if your logic relies on React features, wrap it in a custom Hook named with “use…”; otherwise, a regular function suffices.

3. When should you use useLayoutEffect instead of useEffect?

Use useLayoutEffect when you need to make DOM measurements or modifications before the browser paints, for example, syncing layout or animations to avoid visual flicker. Otherwise, use useEffect for standard side effects like data fetching or subscriptions, which can run after the paint.

Think you know React Hooks? If your mind immediately jumps to useState and useEffect, you’re only scratching the surface. React offers a treasure of powerful hooks that can boost your performance, simplify complex logic, and make your code cleaner than ever. In this blog, we’ll uncover 9 advanced hooks that go beyond the basics, complete with examples you can start using today.

Hooks

React Hooks

(Img Source)

Hooks are special functions in React that let you use state, lifecycle, and other React features in functional components, without writing class components. They were introduced in React 16.8 and allow you to use features that were previously only available in class components.

We all know about the basic and most used hooks mentioned below:

  • useState: useState hook in React JS is a fundamental hook that allows functional components to manage and update state.

  • useEffect: useEffect is a React Hook that allows functional components to perform side effects (data fetching, subscriptions, DOM updates).

  • useContext: useContext in React JS is a Hook that allows functional components to subscribe to a React Context and read its value. It is part of the React Context API, which provides a way to share data across the component tree without having to manually pass props down through every level of the hierarchy (a problem known as "prop drilling").

  • useRef: useRef hook in React.js is a built-in Hook that provides a way to create a mutable reference to an element or a value that persists across renders without triggering re-renders of the component. 

There are many more useful hooks that can be used based on your requirement.

1. useMemo 

  • It is a React Hook that optimizes performance by memoizing (caching/storing) the result of a computationally expensive function or calculation.

  • It prevents unnecessary re-execution of this function on every component re-render, ensuring it only runs when its dependencies change.  

Instead of using this

  const [multiply, setMultiply] = useState<number>(0); 


  useEffect(() => { 
    setMultiply(num * 5); 
  }, [num]);

Use this

const multiply = useMemo(() => { 
    return num * 5; 
  }, [num]);

As you can see above using useMemo hook we don’t need to create a separete state and useEffect for updating state , it simply returns the value updated by the function on dependency change. 

2. useCallback

  • It is a React Hook that memoizes a function definition between renders.  

  • It is used to optimize performance by preventing unnecessary re-creation of functions, especially when those functions are passed as props to child components or are dependencies in other Hooks like useEffect.

Insted of using this

  const handleClick = () => { 
    setCount((prevCount) => prevCount + 1); 
  };

Use this

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

3. useReducer

  • useReducer Hook in React is an alternative to useState for managing component state.T

  • his hook particularly useful when state logic becomes complex or involves multiple related state values.

  • It uses reducer function.

  • You define a reducer function that takes the current state and an action object as arguments.

  • This function contains the logic for how the state should be updated based on the action dispatched.

  • It returns an array containing:
    1. The current state.
    Here current state is an updated state returned by the function that is paased to useReducer hook
    2. A dispatch function.
    A dispatch function lets you update the state to a different value and trigger a re-render. You need to pass the action as the only argument to the dispatch function.

"use client"; 
import { Box } from "@mui/material"; 
import { useReducer } from "react"; 

function reducer(state, action) { 
  switch (action.type) { 
    case "increment": 
    default: 
      return state; 
  } 
} 

export default function Counter() { 
  const [count, dispatch] = useReducer(reducer, 0); 

  return ( 
    <Box> 
      <button onClick={() => dispatch({ type: "increment" })}> 
        Count: {count} 
      </button> 
    </Box> 
  ); 
}

4. useId 

  • It is a React Hook used for generating unique IDs that can be passed to accessibility attributes within your components . 

  • Ids generated by this hook remain stable across server-side and client-side rendering which is too good for hydration. 

  • Its primary purpose is to provide unique IDs for accessibility attributes like id, htmlFor, aria-labelledby, and aria-describedby.

Instead of using this

 <FormControl> 
        <InputLabel htmlFor="my-input">Email address</InputLabel> 
        <Input id="my-input" aria-describedby="my-helper-text" /> 
        <FormHelperText id="my-helper-text"> 
          We'll never share your email. 
        </FormHelperText> 
 </FormControl>

Use this

  //generate ids  
  const id = useId();  
  const inputId = `${id}-input`; 
  const helperId = `${id}-helper`; 

     //use it 
   <FormControl>  
        <InputLabel htmlFor={inputId}>Email address</InputLabel> 
        <Input id={inputId} aria-describedby={helperId} /> 
        <FormHelperText id={helperId}> 
          We'll never share your email. 
        </FormHelperText> 
   </FormControl>

5. useLayoutEffect

  • This react hook is same as useEffect, the only difference is in its execution timing. 

  • useEffect runs asynchronously after the browser has painted the screen. 

  • useLayoutEffect runs synchronously immediately after all DOM mutations have been applied by React, but before the browser paints the screen. 

Instead of using this

  useEffect(() => { 
    if (boxRef.current) { 
      boxRef.current.style.backgroundColor = "blue"; 
    } 
  }, []);

Use this

  useLayoutEffect(() => { 
    if (boxRef.current) { 
      boxRef.current.style.backgroundColor = "blue"; 
    } 
  }, []);

6. useImperativeHandle 

  • React Hook that allows a child component to expose a limited, custom interface to a parent component when a ref is attached. 

  • We need to use 3 things here :
    1. useRef
    2. forwardRef
    3. useImperativeHandle 

Here

  • useRef is used in parent component that creates a link between parent and child component or components.

  • forwardRef is used in child component and used for receiving ref from parent to childcomponent so that parent component can have the special access to child component’s DOM node or custom functions (via useImperativeHandle). 

  • useImperativeHanle is also used inside child component used for specifying the accessibility of child component’s methods or values that can be used by parent component. 

Page.tsx (parent component)

"use client"; 
import React, { useRef } from "react"; 
import { Button } from "@mui/material"; 
import Child from "@/components/Child"; 

const page = () => { 
  const btnRef = useRef<HTMLInputElement|null>(null); 
  return ( 
    <div> 
      <Child ref={btnRef} /> 
      <Button variant="contained" onClick={() => btnRef.current.hi()}> 
        Click 
      </Button> 
    </div> 
  ); 
}; 

export default page;

Child.tsx (child component)

import React, { forwardRef, useImperativeHandle } from "react"; 
import { Typography } from "@mui/material"; 

function Child(props: any, ref: React.Ref<any>) { 
  useImperativeHandle( 
    ref, 
    () => ({ 
      hi: () => alert("Hello"), 
    }), 
    [] 
  ); 

  return ( 
    <div> 
      <Typography sx={{ color: "black" }}>Child Component</Typography> 
    </div>

7. useActionState

  • The useActionState hook in React is a new addition, primarily designed to streamline the management of state updates based on the results of form actions.

  • It simplifies handling asynchronous operations related to forms, such as submissions, by providing a direct way to manage the state and feedback (like loading or error states).

  • This hook is used for enabling seamless form handling with state management between the server and client.

  • It is designed to streamline state updates based on the result of a form action.

  • It takes two arguments:
    1. initialState: The initial value for the state managed by the hook.
    2. action function: This is the function that will be executed when the form action is triggered (e.g., when a form is submitted). It receives the previousState and formData as arguments.

  • It returns an array containing:
    1. state: The current state value, which is updated based on the return value of the action function.
    2. formAction: A new function that you pass to the formAction attribute of your form element or a button's formAction attribute.
    3. isPending (optional): A boolean indicating whether the action is currently in progress.

"use client"; 
import { useActionState } from "react"; 
import { Box, Button, TextField, Alert } from "@mui/material"; 

export async function submitAction( 
  prevState: { error?: string; success?: string }, 
  formData: FormData 
): Promise<{ error?: string; success?: string }> { 
  const email = formData.get("email")?.toString(); 
  const password = formData.get("password")?.toString(); 

  if (!email || !password) { 
    return { error: "Email and password are required" }; 
  } 
  if (password.length < 6) { 
    return { error: "Password too short" }; 
  } 
  await new Promise((res) => setTimeout(res, 1000)); 
  return { success: "User registered successfully!" }; 
} 
export default function RegisterPage() { 
  const initialState = { 
    error: "", 
    success: "", 
  }; 
 
  const [state, formAction, isPending] = useActionState( 
    submitAction, 
    initialState 
  ); 
 
  return ( 
    <Box 
      component="form" 
      action={formAction} 
      sx={{ 
        maxWidth: 400, 
        mx: "auto", 
        display: "flex", 
        flexDirection: "column", 
        gap: 2, 
        p: 3, 
        border: "1px solid #e0e0e0", 
      }} 
    > 
      {state?.error && <Alert severity="error">{state.error}</Alert>} 
      {state?.success && <Alert severity="success">{state.success}</Alert>} 
      <TextField label="Email" name="email" type="email" required fullWidth/> 
      <TextField 
        label="Password" 
        name="password" 
        type="password" 
        required 
        fullWidth 
      /> 
      <Button 
        type="submit" 
        variant="contained" 
        color="primary" 
        disabled={isPending} 
      > 
        {isPending ? "Registering..." : "Register"} 
      </Button> 
    </Box> 
  ); 
}

As you can see in the above example using this hook we don’t need to to create a separate state for storing and updating data as well as for loading state...we can simply get all input Feilds data in formData and handle those data using the function that is passed to useActionState hook. 

8. useLinkStatus

  • This react hook is used to monitor the status of a link, such as whether it is currently being prefetched, visited, or idle.

  • It returns an object containing a pending boolean property, which is true when the link is in a pending state and false otherwise.

  • In modern single-page apps, a user might click on a link and nothing seems to happen that leads bad user experience.

  • Using this hook we can display some message , loader , spinner and any custom component when there is a blocker while navigating to a link.

  • useLinkStatus is useful in the following situations:
    1. Prefetching is disabled or still in progress: This ensures that there is some delay, allowing your pending state logic to execute and be visible to the user.
    2. The destination route is dynamic and doesn't include a loading.js file: This again ensures there is a delay for the pending state logic. The loading.js file shouldn't be available because it would show up instead of the pending state logic.

"use client"; 
import Link, { useLinkStatus } from "next/link"; 

function MyLinkComponent() { 
  const { pending } = useLinkStatus(); 
 
  return ( 
    <Link href="/actionState"> 
      Click me {pending && <span>(Loading...)</span>} 
    </Link> 
  ); 
} 

export default MyLinkComponent;

9. use

  • React 19 introduces a significant new hook called use()

  • It handle asynchronous operations, particularly data fetching and context consumption. 

  • It allows you to directly read the value of a promise or consume context within a component's render phase.

import { use } from "react"; 

async function fetchData() { 
  const response = await fetch("https://jsonplaceholder.typicode.com/posts"); 
  return response.json(); 
} 

function MyServerComponent() { 
  const data = use(fetchData()); // Directly await the promise 
  return ( 
    <div> 
      <h1>Data:</h1> 
      <pre>{JSON.stringify(data, null, 2)}</pre> 
    </div> 
  ); 
} 

export default MyServerComponent;

As you can see above, we don't need to create a separate state and run useEffect for storing and updating data , we simply created async function that fetch data while component render phase storing it in a variable using this hook and using it directly.

Conclusion

React Hooks have completely transformed the way developers build functional components, moving far beyond just useState and useEffect. By exploring advanced hooks like useMemo, useCallback, useReducer, useId, useLayoutEffect, useImperativeHandle, useActionState, useLinkStatus, and the new use() hook in React 19, you can write cleaner code, improve performance, and handle complex logic with ease. Mastering these React Hooks will not only streamline your development process but also help you create faster, scalable, and more efficient React applications.

Frequently Asked Questions

1. Can you use Hooks in class components?

No, you cannot use React Hooks inside class components. Hooks are designed exclusively for functional components and must follow the rules of hooks (e.g. only call them at the top level). If you're working in class components, you’ll need to convert them to functions to leverage Hooks.

2. What’s the difference between a custom Hook and a regular function?

A custom Hook lets you encapsulate stateful logic using built-in React Hooks, like useState or useEffect, and reuse that logic across components. A plain JavaScript function doesn’t offer access to React lifecycle or state. In short, if your logic relies on React features, wrap it in a custom Hook named with “use…”; otherwise, a regular function suffices.

3. When should you use useLayoutEffect instead of useEffect?

Use useLayoutEffect when you need to make DOM measurements or modifications before the browser paints, for example, syncing layout or animations to avoid visual flicker. Otherwise, use useEffect for standard side effects like data fetching or subscriptions, which can run after the paint.

Think you know React Hooks? If your mind immediately jumps to useState and useEffect, you’re only scratching the surface. React offers a treasure of powerful hooks that can boost your performance, simplify complex logic, and make your code cleaner than ever. In this blog, we’ll uncover 9 advanced hooks that go beyond the basics, complete with examples you can start using today.

Hooks

React Hooks

(Img Source)

Hooks are special functions in React that let you use state, lifecycle, and other React features in functional components, without writing class components. They were introduced in React 16.8 and allow you to use features that were previously only available in class components.

We all know about the basic and most used hooks mentioned below:

  • useState: useState hook in React JS is a fundamental hook that allows functional components to manage and update state.

  • useEffect: useEffect is a React Hook that allows functional components to perform side effects (data fetching, subscriptions, DOM updates).

  • useContext: useContext in React JS is a Hook that allows functional components to subscribe to a React Context and read its value. It is part of the React Context API, which provides a way to share data across the component tree without having to manually pass props down through every level of the hierarchy (a problem known as "prop drilling").

  • useRef: useRef hook in React.js is a built-in Hook that provides a way to create a mutable reference to an element or a value that persists across renders without triggering re-renders of the component. 

There are many more useful hooks that can be used based on your requirement.

1. useMemo 

  • It is a React Hook that optimizes performance by memoizing (caching/storing) the result of a computationally expensive function or calculation.

  • It prevents unnecessary re-execution of this function on every component re-render, ensuring it only runs when its dependencies change.  

Instead of using this

  const [multiply, setMultiply] = useState<number>(0); 


  useEffect(() => { 
    setMultiply(num * 5); 
  }, [num]);

Use this

const multiply = useMemo(() => { 
    return num * 5; 
  }, [num]);

As you can see above using useMemo hook we don’t need to create a separete state and useEffect for updating state , it simply returns the value updated by the function on dependency change. 

2. useCallback

  • It is a React Hook that memoizes a function definition between renders.  

  • It is used to optimize performance by preventing unnecessary re-creation of functions, especially when those functions are passed as props to child components or are dependencies in other Hooks like useEffect.

Insted of using this

  const handleClick = () => { 
    setCount((prevCount) => prevCount + 1); 
  };

Use this

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

3. useReducer

  • useReducer Hook in React is an alternative to useState for managing component state.T

  • his hook particularly useful when state logic becomes complex or involves multiple related state values.

  • It uses reducer function.

  • You define a reducer function that takes the current state and an action object as arguments.

  • This function contains the logic for how the state should be updated based on the action dispatched.

  • It returns an array containing:
    1. The current state.
    Here current state is an updated state returned by the function that is paased to useReducer hook
    2. A dispatch function.
    A dispatch function lets you update the state to a different value and trigger a re-render. You need to pass the action as the only argument to the dispatch function.

"use client"; 
import { Box } from "@mui/material"; 
import { useReducer } from "react"; 

function reducer(state, action) { 
  switch (action.type) { 
    case "increment": 
    default: 
      return state; 
  } 
} 

export default function Counter() { 
  const [count, dispatch] = useReducer(reducer, 0); 

  return ( 
    <Box> 
      <button onClick={() => dispatch({ type: "increment" })}> 
        Count: {count} 
      </button> 
    </Box> 
  ); 
}

4. useId 

  • It is a React Hook used for generating unique IDs that can be passed to accessibility attributes within your components . 

  • Ids generated by this hook remain stable across server-side and client-side rendering which is too good for hydration. 

  • Its primary purpose is to provide unique IDs for accessibility attributes like id, htmlFor, aria-labelledby, and aria-describedby.

Instead of using this

 <FormControl> 
        <InputLabel htmlFor="my-input">Email address</InputLabel> 
        <Input id="my-input" aria-describedby="my-helper-text" /> 
        <FormHelperText id="my-helper-text"> 
          We'll never share your email. 
        </FormHelperText> 
 </FormControl>

Use this

  //generate ids  
  const id = useId();  
  const inputId = `${id}-input`; 
  const helperId = `${id}-helper`; 

     //use it 
   <FormControl>  
        <InputLabel htmlFor={inputId}>Email address</InputLabel> 
        <Input id={inputId} aria-describedby={helperId} /> 
        <FormHelperText id={helperId}> 
          We'll never share your email. 
        </FormHelperText> 
   </FormControl>

5. useLayoutEffect

  • This react hook is same as useEffect, the only difference is in its execution timing. 

  • useEffect runs asynchronously after the browser has painted the screen. 

  • useLayoutEffect runs synchronously immediately after all DOM mutations have been applied by React, but before the browser paints the screen. 

Instead of using this

  useEffect(() => { 
    if (boxRef.current) { 
      boxRef.current.style.backgroundColor = "blue"; 
    } 
  }, []);

Use this

  useLayoutEffect(() => { 
    if (boxRef.current) { 
      boxRef.current.style.backgroundColor = "blue"; 
    } 
  }, []);

6. useImperativeHandle 

  • React Hook that allows a child component to expose a limited, custom interface to a parent component when a ref is attached. 

  • We need to use 3 things here :
    1. useRef
    2. forwardRef
    3. useImperativeHandle 

Here

  • useRef is used in parent component that creates a link between parent and child component or components.

  • forwardRef is used in child component and used for receiving ref from parent to childcomponent so that parent component can have the special access to child component’s DOM node or custom functions (via useImperativeHandle). 

  • useImperativeHanle is also used inside child component used for specifying the accessibility of child component’s methods or values that can be used by parent component. 

Page.tsx (parent component)

"use client"; 
import React, { useRef } from "react"; 
import { Button } from "@mui/material"; 
import Child from "@/components/Child"; 

const page = () => { 
  const btnRef = useRef<HTMLInputElement|null>(null); 
  return ( 
    <div> 
      <Child ref={btnRef} /> 
      <Button variant="contained" onClick={() => btnRef.current.hi()}> 
        Click 
      </Button> 
    </div> 
  ); 
}; 

export default page;

Child.tsx (child component)

import React, { forwardRef, useImperativeHandle } from "react"; 
import { Typography } from "@mui/material"; 

function Child(props: any, ref: React.Ref<any>) { 
  useImperativeHandle( 
    ref, 
    () => ({ 
      hi: () => alert("Hello"), 
    }), 
    [] 
  ); 

  return ( 
    <div> 
      <Typography sx={{ color: "black" }}>Child Component</Typography> 
    </div>

7. useActionState

  • The useActionState hook in React is a new addition, primarily designed to streamline the management of state updates based on the results of form actions.

  • It simplifies handling asynchronous operations related to forms, such as submissions, by providing a direct way to manage the state and feedback (like loading or error states).

  • This hook is used for enabling seamless form handling with state management between the server and client.

  • It is designed to streamline state updates based on the result of a form action.

  • It takes two arguments:
    1. initialState: The initial value for the state managed by the hook.
    2. action function: This is the function that will be executed when the form action is triggered (e.g., when a form is submitted). It receives the previousState and formData as arguments.

  • It returns an array containing:
    1. state: The current state value, which is updated based on the return value of the action function.
    2. formAction: A new function that you pass to the formAction attribute of your form element or a button's formAction attribute.
    3. isPending (optional): A boolean indicating whether the action is currently in progress.

"use client"; 
import { useActionState } from "react"; 
import { Box, Button, TextField, Alert } from "@mui/material"; 

export async function submitAction( 
  prevState: { error?: string; success?: string }, 
  formData: FormData 
): Promise<{ error?: string; success?: string }> { 
  const email = formData.get("email")?.toString(); 
  const password = formData.get("password")?.toString(); 

  if (!email || !password) { 
    return { error: "Email and password are required" }; 
  } 
  if (password.length < 6) { 
    return { error: "Password too short" }; 
  } 
  await new Promise((res) => setTimeout(res, 1000)); 
  return { success: "User registered successfully!" }; 
} 
export default function RegisterPage() { 
  const initialState = { 
    error: "", 
    success: "", 
  }; 
 
  const [state, formAction, isPending] = useActionState( 
    submitAction, 
    initialState 
  ); 
 
  return ( 
    <Box 
      component="form" 
      action={formAction} 
      sx={{ 
        maxWidth: 400, 
        mx: "auto", 
        display: "flex", 
        flexDirection: "column", 
        gap: 2, 
        p: 3, 
        border: "1px solid #e0e0e0", 
      }} 
    > 
      {state?.error && <Alert severity="error">{state.error}</Alert>} 
      {state?.success && <Alert severity="success">{state.success}</Alert>} 
      <TextField label="Email" name="email" type="email" required fullWidth/> 
      <TextField 
        label="Password" 
        name="password" 
        type="password" 
        required 
        fullWidth 
      /> 
      <Button 
        type="submit" 
        variant="contained" 
        color="primary" 
        disabled={isPending} 
      > 
        {isPending ? "Registering..." : "Register"} 
      </Button> 
    </Box> 
  ); 
}

As you can see in the above example using this hook we don’t need to to create a separate state for storing and updating data as well as for loading state...we can simply get all input Feilds data in formData and handle those data using the function that is passed to useActionState hook. 

8. useLinkStatus

  • This react hook is used to monitor the status of a link, such as whether it is currently being prefetched, visited, or idle.

  • It returns an object containing a pending boolean property, which is true when the link is in a pending state and false otherwise.

  • In modern single-page apps, a user might click on a link and nothing seems to happen that leads bad user experience.

  • Using this hook we can display some message , loader , spinner and any custom component when there is a blocker while navigating to a link.

  • useLinkStatus is useful in the following situations:
    1. Prefetching is disabled or still in progress: This ensures that there is some delay, allowing your pending state logic to execute and be visible to the user.
    2. The destination route is dynamic and doesn't include a loading.js file: This again ensures there is a delay for the pending state logic. The loading.js file shouldn't be available because it would show up instead of the pending state logic.

"use client"; 
import Link, { useLinkStatus } from "next/link"; 

function MyLinkComponent() { 
  const { pending } = useLinkStatus(); 
 
  return ( 
    <Link href="/actionState"> 
      Click me {pending && <span>(Loading...)</span>} 
    </Link> 
  ); 
} 

export default MyLinkComponent;

9. use

  • React 19 introduces a significant new hook called use()

  • It handle asynchronous operations, particularly data fetching and context consumption. 

  • It allows you to directly read the value of a promise or consume context within a component's render phase.

import { use } from "react"; 

async function fetchData() { 
  const response = await fetch("https://jsonplaceholder.typicode.com/posts"); 
  return response.json(); 
} 

function MyServerComponent() { 
  const data = use(fetchData()); // Directly await the promise 
  return ( 
    <div> 
      <h1>Data:</h1> 
      <pre>{JSON.stringify(data, null, 2)}</pre> 
    </div> 
  ); 
} 

export default MyServerComponent;

As you can see above, we don't need to create a separate state and run useEffect for storing and updating data , we simply created async function that fetch data while component render phase storing it in a variable using this hook and using it directly.

Conclusion

React Hooks have completely transformed the way developers build functional components, moving far beyond just useState and useEffect. By exploring advanced hooks like useMemo, useCallback, useReducer, useId, useLayoutEffect, useImperativeHandle, useActionState, useLinkStatus, and the new use() hook in React 19, you can write cleaner code, improve performance, and handle complex logic with ease. Mastering these React Hooks will not only streamline your development process but also help you create faster, scalable, and more efficient React applications.

Frequently Asked Questions

1. Can you use Hooks in class components?

No, you cannot use React Hooks inside class components. Hooks are designed exclusively for functional components and must follow the rules of hooks (e.g. only call them at the top level). If you're working in class components, you’ll need to convert them to functions to leverage Hooks.

2. What’s the difference between a custom Hook and a regular function?

A custom Hook lets you encapsulate stateful logic using built-in React Hooks, like useState or useEffect, and reuse that logic across components. A plain JavaScript function doesn’t offer access to React lifecycle or state. In short, if your logic relies on React features, wrap it in a custom Hook named with “use…”; otherwise, a regular function suffices.

3. When should you use useLayoutEffect instead of useEffect?

Use useLayoutEffect when you need to make DOM measurements or modifications before the browser paints, for example, syncing layout or animations to avoid visual flicker. Otherwise, use useEffect for standard side effects like data fetching or subscriptions, which can run after the paint.

Explore other blogs

Explore other blogs

let's get in touch

Have a Project idea?

Connect with us for a free consultation !

Confidentiality with NDA

Understanding the core business.

Brainstorm with our leaders

Daily & Weekly Updates

Super competitive pricing

let's get in touch

Have a Project idea?

Connect with us for a free consultation !

Confidentiality with NDA

Understanding the core business.

Brainstorm with our leaders

Daily & Weekly Updates

Super competitive pricing