Правила использования React хуков: как писать код без ошибок

8 апреля 2026 г.

React хуки появились в версии 16.8 и позволили использовать состояние и другие возможности React без написания классов. Однако хуки работают не как обычные функции — у них есть строгие правила. Нарушение этих правил приводит к трудноуловимым багам и нестабильной работе приложения. В этой статье разберём два главных правила использования хуков, почему они существуют и как их соблюдать на практике.

Два главных правила React хуков

Официальная документация React формулирует два обязательных правила для всех хуков (useState, useEffect, useContext, useReducer и других):

  1. Вызывайте хуки только на верхнем уровне.
  2. Вызывайте хуки только из React-функций.

Рассмотрим каждое правило подробнее.

1. Вызов только на верхнем уровне

Не вызывайте хуки внутри циклов, условий или вложенных функций. Хуки должны вызываться в одном и том же порядке при каждом рендере компонента. Только так React может правильно сохранять состояние между множественными вызовами useState и useEffect.

Неправильно:

function MyComponent({ items }) {
  if (items.length === 0) {
    const [isEmpty, setIsEmpty] = useState(true); // Ошибка!
  }
  
  for (let i = 0; i < items.length; i++) {
    const [itemState, setItemState] = useState(null); // Ошибка!
  }
  
  return <div>...</div>;
}

Правильно:

function MyComponent({ items }) {
  const [isEmpty, setIsEmpty] = useState(items.length === 0);
  
  // Если нужно состояние для каждого элемента — используй useReducer или один объект
  const [itemsState, setItemsState] = useState(() => 
    items.map(() => null)
  );
  
  return <div>...</div>;
}

2. Вызов только в React-функциях

Не вызывайте хуки в обычных JavaScript-функциях. Есть только два места, где хуки разрешены:

  • В функциональных компонентах React.
  • В кастомных хуках (функциях, имя которых начинается с use, например useWindowWidth).

Неправильно:

function formatUserData(user) {
  const [data, setData] = useState(user); // Ошибка!
  return data.toUpperCase();
}

function MyComponent() {
  return <div>{formatUserData(user)}</div>;
}

Правильно:

function useFormattedUser(user) {
  const [data, setData] = useState(user);
  
  useEffect(() => {
    setData(user.toUpperCase());
  }, [user]);
  
  return data;
}

function MyComponent({ user }) {
  const formattedUser = useFormattedUser(user);
  return <div>{formattedUser}</div>;
}

Почему эти правила так важны?

React опирается на порядок вызова хуков, чтобы правильно связывать каждое состояние с соответствующим хуком. Внутри React каждый хук в компоненте получает индекс — по порядку их вызова. При следующем рендере React ожидает, что хуки будут вызваны в той же последовательности.

Если ты поместишь хук внутрь условия, порядок может нарушиться. Например, при первом рендере условие истинно и хук вызывается, а при втором — ложно, и хук пропускается. Тогда React не сможет сопоставить сохранённое состояние с правильным хуком, и данные «съедут» — состояние одного хука попадёт в другой.

Та же логика работает для циклов: количество вызовов хуков должно быть одинаковым при каждом рендере. Если массив items изменит длину, количество вызовов хуков изменится, и React сломается.

Распространённые ошибки и как их избежать

Ошибка 1: условный вызов useEffect или useState
Вместо условия вокруг хука используй условие внутри хука.

// ❌ Плохо
if (isVisible) {
  useEffect(() => {
    console.log('Visible');
  }, [isVisible]);
}

// ✅ Хорошо
useEffect(() => {
  if (isVisible) {
    console.log('Visible');
  }
}, [isVisible]);

Ошибка 2: вызов хука в обработчике события
Обработчики событий не являются React-функциями и не вызываются при каждом рендере.

// ❌ Плохо
function Button() {
  const handleClick = () => {
    const [clicked, setClicked] = useState(false); // Ошибка!
    setClicked(true);
  };
  return <button onClick={handleClick}>Click</button>;
}

// ✅ Хорошо
function Button() {
  const [clicked, setClicked] = useState(false);
  const handleClick = () => setClicked(true);
  return <button onClick={handleClick}>Click</button>;
}

Ошибка 3: хук внутри useMemo или useCallback
Эти функции тоже не гарантируют стабильный порядок вызова.

// ❌ Плохо
const memoized = useMemo(() => {
  const [value, setValue] = useState(0); // Ошибка!
  return value;
}, []);

// ✅ Хорошо — подними хук наверх
const [value, setValue] = useState(0);
const memoized = useMemo(() => value, [value]);

Как ESLint помогает соблюдать правила

React предоставляет официальный плагин eslint-plugin-react-hooks, который автоматически проверяет соблюдение правил хуков. Он найдёт вызовы внутри условий, циклов или обычных функций.

Установка и настройка:

npm install eslint-plugin-react-hooks --save-dev

В файле .eslintrc.js:

module.exports = {
  plugins: ['react-hooks'],
  rules: {
    'react-hooks/rules-of-hooks': 'error',
    'react-hooks/exhaustive-deps': 'warn'
  }
};

Правило exhaustive-deps предупреждает о пропущенных зависимостях в useEffect, useMemo и useCallback — это необязательное, но крайне полезное дополнение.

Итог

Два правила использования React хуков — это не просто рекомендации, а обязательные условия для корректной работы React. Запомни:

  • Вызывай хуки всегда на верхнем уровне компонента или кастомного хука — не внутри условий, циклов или вложенных функций.
  • Вызывай хуки только из функциональных компонентов React или кастомных хуков (с префиксом use).
  • Используй eslint-plugin-react-hooks, чтобы не пропустить нарушение.

Соблюдение этих правил сделает твой код предсказуемым, а состояние компонентов — стабильным. Если ты только начинаешь работать с хуками, настрой линтер сразу — это сэкономит часы отладки в будущем.