CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-eslint-plugin-react-hooks

ESLint plugin that enforces the Rules of Hooks for React applications with additional React Compiler rules

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

core-rules.mddocs/

Core React Hooks Rules

This document covers the two essential React Hooks rules that form the foundation of the eslint-plugin-react-hooks package.

Rules of Hooks Rule

Enforces that React Hooks are only called at the top level of React functions and not inside loops, conditions, or nested functions.

const RulesOfHooksRule: Rule.RuleModule = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforces the Rules of Hooks',
      recommended: true,
      url: 'https://react.dev/reference/rules/rules-of-hooks'
    }
  },
  create(context: RuleContext): NodeVisitor
};

Configuration

{
  "rules": {
    "react-hooks/rules-of-hooks": "error"
  }
}

What It Checks

  • Hooks are only called from React function components
  • Hooks are only called from custom Hooks (functions starting with "use")
  • Hooks are not called inside loops, conditions, or nested functions
  • Hooks are called in the same order every time

Valid Examples

// ✅ Valid: Hook called at top level of component
function MyComponent() {
  const [state, setState] = useState(0);
  useEffect(() => {
    // Effect logic
  });
  return <div>{state}</div>;
}

// ✅ Valid: Hook called at top level of custom hook
function useCustomHook() {
  const [state, setState] = useState(0);
  return state;
}

// ✅ Valid: Hook called in component returned by HOC
function createComponent() {
  return function Component() {
    const [state, setState] = useState(0);
    return <div>{state}</div>;
  };
}

Invalid Examples

// ❌ Invalid: Hook called conditionally
function MyComponent({ condition }) {
  if (condition) {
    const [state, setState] = useState(0); // Error!
  }
}

// ❌ Invalid: Hook called in loop
function MyComponent({ items }) {
  const states = [];
  for (const item of items) {
    states.push(useState(item)); // Error!
  }
}

// ❌ Invalid: Hook called in regular function
function regularFunction() {
  const [state, setState] = useState(0); // Error!
}

Exhaustive Dependencies Rule

Verifies that all dependencies of Hooks like useEffect, useMemo, and useCallback are included in their dependency arrays.

const ExhaustiveDepsRule: Rule.RuleModule = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'verifies the list of dependencies for Hooks like useEffect and similar',
      recommended: true,
      url: 'https://github.com/facebook/react/issues/14920'
    },
    fixable: 'code',
    hasSuggestions: true,
    schema: [
      {
        type: 'object',
        additionalProperties: false,
        properties: {
          additionalHooks: {
            type: 'string'
          }
        }
      }
    ]
  },
  create(context: RuleContext): NodeVisitor
};

Configuration

{
  "rules": {
    "react-hooks/exhaustive-deps": "warn"
  }
}

Advanced Configuration

Configure custom hooks that should be validated:

interface ExhaustiveDepsOptions {
  additionalHooks?: string; // RegExp pattern for custom hooks
  enableDangerousAutofixThisMayCauseInfiniteLoops?: boolean; // Enable legacy autofix
  experimental_autoDependenciesHooks?: string[]; // Experimental auto-dependency hooks
  requireExplicitEffectDeps?: boolean; // Require explicit dependency arrays
}
{
  "rules": {
    "react-hooks/exhaustive-deps": ["warn", {
      "additionalHooks": "(useMyCustomHook|useMyOtherCustomHook)"
    }]
  }
}

What It Checks

  • useEffect dependencies include all values from component scope that are used inside the effect
  • useMemo dependencies include all values used in the computation
  • useCallback dependencies include all values used in the callback
  • Custom hooks (specified in additionalHooks) have correct dependencies

Valid Examples

// ✅ Valid: All dependencies included
function MyComponent({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]); // userId is included in dependencies
  
  const memoizedValue = useMemo(() => {
    return expensiveCalculation(user);
  }, [user]); // user is included in dependencies
  
  const handleClick = useCallback(() => {
    console.log(user.name);
  }, [user]); // user is included in dependencies
}

// ✅ Valid: Empty dependencies for effect that doesn't use any values
function MyComponent() {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('tick');
    }, 1000);
    return () => clearInterval(timer);
  }, []); // No dependencies needed
}

Invalid Examples

// ❌ Invalid: Missing dependency
function MyComponent({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, []); // Error: userId should be in dependencies
}

// ❌ Invalid: Stale closure
function MyComponent() {
  const [count, setCount] = useState(0);
  
  const handleClick = useCallback(() => {
    console.log(count); // This will always log the initial value
  }, []); // Error: count should be in dependencies
}

// ❌ Invalid: Unnecessary dependency
function MyComponent() {
  const [count, setCount] = useState(0);
  const stableValue = 42;
  
  useEffect(() => {
    console.log(count);
  }, [count, stableValue]); // Error: stableValue is not needed
}

Auto-fix Support

The exhaustive-deps rule provides automatic fixes for common dependency issues:

  • Add missing dependencies: Automatically adds missing values to dependency arrays
  • Remove unnecessary dependencies: Removes stable values that don't need to be dependencies
  • Suggest dependency updates: Provides suggestions for complex dependency scenarios

Custom Hook Integration

For custom hooks that follow the same dependency patterns:

// Custom hook that should be validated
function useCustomEffect(callback, deps) {
  useEffect(callback, deps);
}

// Configuration to validate this custom hook
{
  "rules": {
    "react-hooks/exhaustive-deps": ["warn", {
      "additionalHooks": "useCustomEffect"
    }]
  }
}

Rule Helper Types

// Hook detection utilities
function isHookName(name: string): boolean;
function isHook(node: Node): boolean;
function isReactFunction(node: Node): boolean;

// Dependency analysis types
interface DeclaredDependency {
  key: string;
  node: Node;
}

interface Dependency {
  isStable: boolean;
  references: Array<Scope.Reference>;
}

interface DependencyTreeNode {
  isUsed: boolean;
  isSatisfiedRecursively: boolean;
  isSubtreeUsed: boolean;
  children: Map<string, DependencyTreeNode>;
}

docs

compiler-rules.md

configuration.md

core-rules.md

index.md

tile.json