We want to hear from you!Take our 2021 Community Survey!
This site is no longer updated.Go to react.dev

Попередження: некоректний виклик хука

Швидше за все, ви перейшли на цю сторінку, тому що отримали наступне повідомлення про помилку:

Hooks can only be called inside the body of a function component.

Існує три поширених причини, через які ви могли побачити це:

  1. Невідповідність версій React і React DOM у вашому додатку.
  2. Ви порушили правила хуків
  3. Ви маєте більш ніж одну копію React в одному додатку.

Розглянемо кожний з цих випадків.

Невідповідність версій React і React DOM

Можливо ви використовуєте версії react-dom (< 16.8.0) або react-native (< 0.59), які ще не підтримують хуки. Виконайте у терміналі команду npm ls react-dom або npm ls react-native у директорії вашого додатку, щоб перевірити які версії встановлено. Якщо їх виявиться більше, ніж одна, це також може становити проблему (докладніше про це далі).

Порушення правил використання хуків

Ви можете викликати хуки лише тоді, коли React відображує функціональний компонент:

  • ✅ Викликайте їх на верхньому рівні в тілі функціонального компонента.
  • ✅ Викликайте їх на верхньому рівні в тілі користувацького хука.

Дізнайтесь більше про це в правилах хуків.

function Counter() {
  // ✅ Добре: на верхньому рівні функціонального компонента  const [count, setCount] = useState(0);  // ...
}

function useWindowWidth() {
  // ✅ Добре: на верхньому рівні користувацького хука  const [width, setWidth] = useState(window.innerWidth);  // ...
}

Щоб уникнути непорозуміння, використання хуку не підтримується у наступних випадках:

  • 🔴 Не викликайте хуки у класових компонентах
  • 🔴 Не викликайте хуки в обробниках подій
  • 🔴 Не викликайте хуки всередині функцій, переданих до useMemo, useReducer, або useEffect.

Якщо ви порушуєте ці правила, то можна зіткнутися з цією помилкою.

function Bad1() {
  function handleClick() {
    // 🔴 Погано: всередині обробника події (щоб виправити, винесіть виклик назовні!)    const theme = useContext(ThemeContext);  }
  // ...
}

function Bad2() {
  const style = useMemo(() => {
    // 🔴 Погано: всередині useMemo (щоб виправити, винесіть виклик назовні!)    const theme = useContext(ThemeContext);    return createStyle(theme);
  });
  // ...
}

class Bad3 extends React.Component {
  render() {
    // 🔴 Погано: всередині класового компонента    useEffect(() => {})    // ...
  }
}

Ви можете використати плагін eslint-plugin-react-hooks для того, щоб відловити деякі з цих помилок.

Примітка

Користувацькі хуки можуть викликати інші хуки (у тому й полягає їх призначення). Це не викликає проблем, бо користувацькі хуки також мають викликатися лише тоді, коли відображується функціональний компонент.

Дублювання React

Для того, щоб хуки працювали, у вашому додатку потрібно імпортувати той самий модуль react, що імпортується всередині пакету react-dom.

Якщо під час імпорту react ви звертаєтесь до двох різних джерел, то побачите попередження. Це станеться, коли ви випадково матимете дві копії пакету react.

Якщо ви використовуєте Node для керування пакетами, то в корені вашого проекту можна запустити перевірку наступним чином:

npm ls react

У тому випадку, якщо побачите більше ніж один пакет React, вам потрібно з’ясувати, як це трапилось, та виправити це. Наприклад, бібліотека, яку ви використовуєте, неправильно визначила react як залежність (а не peer-залежність). До того часу, як це буде виправлено, вирішення Yarn може стати одним з можливих тимчасових рішень.

Також ви можете спробувати розібратися з цією проблемою, використовуючи режим налагодження й перезапустивши сервер розробки:

// Додайте це до node_modules/react-dom/index.js
window.React1 = require('react');

// Додайте це до файлу з вашим компонентом
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);

Якщо у консолі ви побачите false, це означає, що ви використовуєте дві різні копії React. Обговорення цього питання може підказати вам деякі загальні причини, з якими вже зіткнулась спільнота, та рішення.

Ця проблема також може з’явитись, якщо ви використовуєте команду npm link чи щось подібне. У цьому разі бандлер може “побачити” два React - один у директорії додатку та другий у директорії вашої бібліотеки. Припускаючи, що папки myapp і mylib знаходяться на одному рівні, виконання команди npm link ../myapp/node_modules/react з mylib може розв’язати проблему. Це має змусити бібліотеку використовувати копію React з додатка.

Примітка

Взагалі, React підтримує можливість використання декількох незалежних копій на одній сторінці (наприклад, якщо їх використовують додаток і сторонній віджет). Проблемою це стає лише у тому випадку, коли в рамках однієї програми компонент імпортує одну копію React, а react-dom іншу.

Інші причини

Якщо жодне із запропонованих рішень не спрацювало, будь ласка, залиште коментар у цьому обговоренні та ми спробуємо допомогти. Також спробуйте зробити невеликий приклад, здатний відтворити проблему у тому вигляді, у якому ви з нею зіткнулись.