Предупреждение: некорректный вызов хука
Скорее всего, вы перешли на эту страницу, потому что получили следующее сообщение об ошибке:
Hooks can only be called inside the body of a function component.
Есть три основные причины, по которым вы могли увидеть это предупреждение:
- Несоответствие версий React и React DOM.
- Нарушение правил хуков.
- Наличие более одной копии 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 поддерживает использование нескольких независимых копий на одной странице (например, при одновременном использовании приложения и стороннего виджета). Корректная работа нарушается, если выражение
require('react')
разрешается по-разному между компонентом и копией изreact-dom
, с помощью которой он был отрендерен.
Другие случаи
Если ни одно из решений не помогло, пожалуйста, оставьте комментарий в этом ишью, после чего мы постараемся вам помочь. Попробуйте также создать небольшой пример, который воспроизводит вашу проблему.