logoESLint React
Rules

rules-of-hooks

Full Name in @eslint-react/eslint-plugin

@eslint-react/hooks-extra/rules-of-hooks

Full Name in eslint-plugin-react-hooks-extra

react-hooks-extra/rules-of-hooks

Presets

Not included in any preset.

Description

Enforces the Rules of Hooks.

React Hooks must be called at the top level of a React function component or a custom Hook. They must not be called inside conditions, loops, nested functions, after early returns, in async functions, in class components, or at module level.

The special use() hook is an exception — it is allowed inside conditionals and loops, but not inside try/catch blocks.

Examples

Passing

import { useState, useEffect } from "react";

function MyComponent() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log(count);
  }, [count]);
  return <div>{count}</div>;
}

Passing

import { useState } from "react";

function useCustomHook() {
  const [value, setValue] = useState("");
  return [value, setValue] as const;
}

Passing

import { use } from "react";

function MyComponent({ shouldFetch, promise }) {
  if (shouldFetch) {
    const data = use(promise);
    return <div>{data}</div>;
  }
  return <div>No data</div>;
}

Failing

import { useState } from "react";

function MyComponent({ condition }) {
  // 🔴 React Hook 'useState' is called conditionally.
  if (condition) {
    const [value, setValue] = useState("");
  }
  return null;
}

Failing

import { useEffect } from "react";

function MyComponent({ items }) {
  // 🔴 React Hook 'useEffect' may be executed more than once.
  for (const item of items) {
    useEffect(() => {
      console.log(item);
    }, [item]);
  }
  return null;
}

Failing

import { useState, useEffect } from "react";

function MyComponent({ condition }) {
  if (condition) {
    return null;
  }
  // 🔴 React Hook 'useState' is called after an early return.
  const [value, setValue] = useState("");
  return <div>{value}</div>;
}

Failing

import { useState } from "react";

function MyComponent() {
  const handleClick = () => {
    // 🔴 React Hook 'useState' is called in a nested function.
    const [value, setValue] = useState("");
  };
  return <button onClick={handleClick}>Click</button>;
}

Failing

import { useState } from "react";

// 🔴 React Hook 'useState' is called in an async function.
async function useAsyncHook() {
  const [value, setValue] = useState("");
  return value;
}

Failing

import { useState } from "react";

class MyComponent {
  render() {
    // 🔴 React Hook 'useState' cannot be called in a class component.
    const [value, setValue] = useState("");
    return null;
  }
}

Failing

import { useState } from "react";

// 🔴 React Hook 'useState' cannot be called at the top level.
const [value, setValue] = useState("");

Failing

import { use } from "react";

function MyComponent({ promise }) {
  try {
    // 🔴 React Hook 'use' cannot be called inside a try/catch block.
    const data = use(promise);
    return <div>{data}</div>;
  } catch (e) {
    return <div>Error</div>;
  }
}

Implementation

Further Reading

On this page