← Назад к вопросам

Где нельзя использовать hooks?

2.0 Middle🔥 171 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Где нельзя использовать hooks в React

Hooks имеют строгие правила использования. Они работают только в определенных контекстах, и нарушение этих правил приведет к ошибкам и неожиданному поведению. Это важный вопрос, который показывает глубокое понимание React.

Основное правило: Rules of Hooks

Официальные правила React для hooks:

  1. Вызывай hooks только на верхнем уровне
  2. Вызывай hooks только в React компонентах и кастомных хуках

Место 1: Нельзя в обычных функциях

❌ НЕПРАВИЛЬНО: вызов в обычной функции

// Это обычная функция, не React компонент
function regularFunction() {
  const [count, setCount] = useState(0) // ОШИБКА!
  return count
}

// Результат: Runtime ошибка
// "React Hook "useState" is called in a function "regularFunction" 
// that is neither a React function component nor a custom React Hook"

✅ ПРАВИЛЬНО: в React компоненте

// React компонент (начинается с заглавной буквы)
function RegularComponent() {
  const [count, setCount] = useState(0) // OK!
  return <div>{count}</div>
}

Место 2: Нельзя в условиях

❌ НЕПРАВИЛЬНО: hooks в if/else блоке

function BadComponent({ showCounter }) {
  if (showCounter) {
    const [count, setCount] = useState(0) // ОШИБКА!
  }
  return <div>...</div>
}

// Проблема: порядок вызовов hooks изменяется
// React не может нормально отследить состояние

✅ ПРАВИЛЬНО: state вне условия

function GoodComponent({ showCounter }) {
  const [count, setCount] = useState(0) // На верхнем уровне
  
  if (showCounter) {
    return <div>{count}</div>
  }
  
  return <div>Счетчик скрыт</div>
}

Место 3: Нельзя в циклах

❌ НЕПРАВИЛЬНО: hooks в цикле

function BadList({ items }) {
  // ОШИБКА: количество hooks зависит от items.length
  for (let i = 0; i < items.length; i++) {
    const [selected, setSelected] = useState(false)
  }
  
  return <ul>{/* ... */}</ul>
}

// Проблема: при изменении длины items нарушится порядок hooks
// React будет путать какой state какому элементу принадлежит

✅ ПРАВИЛЬНО: state в компоненте

function GoodList({ items }) {
  const [selectedIds, setSelectedIds] = useState<string[]>([])
  
  return (
    <ul>
      {items.map(item => (
        <ListItem
          key={item.id}
          item={item}
          isSelected={selectedIds.includes(item.id)}
          onSelect={() => {
            setSelectedIds(prev => [...prev, item.id])
          }}
        />
      ))}
    </ul>
  )
}

Место 4: Нельзя в обработчиках событий

❌ НЕПРАВИЛЬНО: hooks в onClick/onChange

function BadForm() {
  const handleChange = () => {
    const [value, setValue] = useState("") // ОШИБКА!
    setValue("new value")
  }
  
  return <input onChange={handleChange} />
}

// Проблема: hooks вызываются каждый раз при событии
// Состояние переинициализируется

✅ ПРАВИЛЬНО: state в компоненте, обработчик его использует

function GoodForm() {
  const [value, setValue] = useState("") // На верхнем уровне
  
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value) // Используем state в обработчике
  }
  
  return <input value={value} onChange={handleChange} />
}

Место 5: Нельзя в try/catch блоках (условные)

❌ НЕПРАВИЛЬНО: hooks в try/catch

function BadComponent() {
  try {
    const [data, setData] = useState(null) // ОШИБКА (может быть скипнут)
  } catch (error) {
    const [error, setError] = useState(null) // ОШИБКА!
  }
  
  return <div></div>
}

✅ ПРАВИЛЬНО: состояние вне try/catch

function GoodComponent() {
  const [data, setData] = useState(null) // На верхнем уровне
  const [error, setError] = useState(null)
  
  useEffect(() => {
    try {
      // Логика внутри эффекта
    } catch (err) {
      setError(err) // Используем setState
    }
  }, [])
  
  return <div></div>
}

Место 6: Нельзя в классовых компонентах

❌ НЕПРАВИЛЬНО: hooks в классовом компоненте

class BadComponent extends React.Component {
  render() {
    const [count, setCount] = useState(0) // ОШИБКА!
    return <div>{count}</div>
  }
}

✅ ПРАВИЛЬНО: либо функциональный компонент, либо использовать lifecycle методы

// Функциональный компонент с hooks
function GoodComponent() {
  const [count, setCount] = useState(0)
  return <div>{count}</div>
}

// ИЛИ классовый компонент без hooks
class ClassComponent extends React.Component {
  state = { count: 0 }
  
  render() {
    return <div>{this.state.count}</div>
  }
}

Место 7: Нельзя в асинхронных функциях

❌ НЕПРАВИЛЬНО: hooks в async function

function BadComponent() {
  // Функция async, это изменяет поведение
  const handleAsync = async () => {
    const [data, setData] = useState(null) // ОШИБКА!
  }
  
  return <button onClick={handleAsync}>Load</button>
}

✅ ПРАВИЛЬНО: hooks на верхнем уровне

function GoodComponent() {
  const [data, setData] = useState(null) // На верхнем уровне
  
  const handleAsync = async () => {
    const response = await fetch("/api/data")
    const result = await response.json()
    setData(result) // Используем state setter
  }
  
  return <button onClick={handleAsync}>Load</button>
}

Сложный пример: нарушение правил

❌ НЕПРАВИЛЬНО: множество нарушений

function ComplexBadComponent({ users, showDetails }) {
  const renderUser = (user) => {
    // Это функция (не компонент)
    const [expanded, setExpanded] = useState(false) // ОШИБКА 1
    
    return (
      <div>
        {showDetails && (
          // Условие
          const [details, setDetails] = useState(null) // ОШИБКА 2
        )}
        {users.map(u => {
          // Цикл
          const [selected, setSelected] = useState(false) // ОШИБКА 3
          return <div key={u.id}>{u.name}</div>
        })}
      </div>
    )
  }
  
  return <div>{users.map(renderUser)}</div>
}

✅ ПРАВИЛЬНО: правильная структура

function ComplexGoodComponent({ users, showDetails }) {
  const [expanded, setExpanded] = useState(false) // На верхнем уровне
  const [details, setDetails] = useState(null)
  const [selectedIds, setSelectedIds] = useState<string[]>([])
  
  return (
    <div>
      {users.map(user => (
        <UserItem
          key={user.id}
          user={user}
          expanded={expanded}
          details={details}
          isSelected={selectedIds.includes(user.id)}
          onToggleSelect={() => {
            setSelectedIds(prev =>
              prev.includes(user.id)
                ? prev.filter(id => id !== user.id)
                : [...prev, user.id]
            )
          }}
        />
      ))}
    </div>
  )
}

function UserItem({ user, isSelected, onToggleSelect }) {
  return (
    <div>
      <input
        type="checkbox"
        checked={isSelected}
        onChange={onToggleSelect}
      />
      {user.name}
    </div>
  )
}

Исключение: Кастомные hooks

✅ ПРАВИЛЬНО: hooks можно использовать в кастомных хуках

// Кастомный hook - это обычная функция, но начинается с "use"
function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue) // OK!
  
  const increment = () => setCount(prev => prev + 1)
  const decrement = () => setCount(prev => prev - 1)
  
  return { count, increment, decrement }
}

// Использование в компоненте
function MyComponent() {
  const { count, increment } = useCounter(0) // OK!
  
  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>+</button>
    </div>
  )
}

ESLint правила

Для защиты от ошибок используй ESLint плагин:

npm install --save-dev eslint-plugin-react-hooks
// .eslintrc.json
{
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn"
  }
}

Это предупредит о нарушениях правил hooks.

Вывод

Hooks можно использовать ТОЛЬКО:

  1. На верхнем уровне React компонента
  2. На верхнем уровне кастомного hook
  3. Вне условий, циклов и вложенных функций

Hooks НЕЛЬЗЯ использовать:

  • В обычных функциях
  • В условиях (if/else)
  • В циклах (for/while)
  • В обработчиках событий
  • В классовых компонентах
  • Внутри try/catch блоков

Эти правила существуют потому, что React полагается на порядок вызовов hooks для правильного управления состоянием. Нарушение порядка приводит к ошибкам.