React-Hooks

Why React Hooks Do Not Work Inside Async Functions

Editorial Cover

While developing a new project using Next.js, I encountered an issue where React Hooks could not be used inside an asynchronous function. After researching this problem, I found that it is related to the Rules of Hooks defined by React.


React Rules of Hooks

According to the official React documentation, hooks must always be called in the same order during every render.

React depends on this consistent order to correctly associate state and effects with each hook call.

If the order changes between renders, React may lose track of which state belongs to which hook, leading to errors such as:

  • "Rendered fewer hooks than expected"
  • "Rendered more hooks than expected"
  • State mismatches

Common Violations of the Rules of Hooks

Some common patterns that violate these rules include:

  • Calling hooks inside conditions (if, else, ternary operators)
  • Calling hooks inside loops (for, while)
  • Calling hooks after early returns
  • Calling hooks inside callbacks or event handlers
  • Calling hooks inside async functions
  • Calling hooks inside class methods
  • Calling hooks at the module level

Hooks Inside Async Functions

One common mistake is attempting to use hooks inside an async function.

Hooks must run during the rendering of a React function component. React relies on the order of hooks during this render process to manage state and effects correctly.

If hooks are called inside an async function, they are executed outside the component’s render flow, which breaks React’s hook tracking mechanism.

Because of this, hooks can only be called inside:

  • React function components
  • Custom hooks

Not inside regular JavaScript functions such as async functions.


Solution: Using Async Logic Inside useEffect

A common solution is to keep the useEffect callback synchronous while calling an internal async function.

Example

import { useEffect, useState } from "react";

export default function Example() {
  const [data, setData] = useState(null);

  useEffect(() => {
    async function fetchData() {
      const res = await fetch("/api/data");
      const json = await res.json();
      setData(json);
    }

    fetchData();
  }, []);

  return <div>{data ? JSON.stringify(data) : "Loading..."}</div>;
}

Feedbacks

0 feedbacks