Правила хуков
Хуки — это новое дополнение в React 16.8. Они позволяют использовать состояние и другие возможности React без написания класса.
Хуки — это функции JavaScript, но при их использовании нужно соблюдать два правила. Мы предоставляем плагин линтера eslint-plugin-react-hooks для автоматического соблюдения этих правил:
Вызывать хуки только на верхнем уровне
Не вызывайте хуки внутри циклов, условных операторов или вложенных функций. Вместо этого всегда используйте хуки на верхнем уровне вашей функции React, перед любыми ранними возвратами. Следуя этому правилу, вы обеспечиваете, что хуки вызываются в одном и том же порядке каждый раз, когда компонент отрисовывается. Это позволяет React правильно сохранять состояние хуков между множеством вызовов useState и useEffect. (Если вы хотите узнать об этом подробнее, мы объясним это подробно ниже.)
Вызывать хуки только из функций React
Не вызывайте хуки из обычных функций JavaScript. Вместо этого вы можете:
- ✅ Вызывать хуки из компонентов функций React.
- ✅ Вызывать хуки из пользовательских хуков (мы узнаем об этом на следующей странице).
Следуя этому правилу, вы обеспечиваете, что вся логика со состоянием в компоненте явно видна из исходного кода.
Плагин ESLint
Мы выпустили плагин ESLint под названием eslint-plugin-react-hooks, который накладывает эти два правила. Вы можете добавить этот плагин в свой проект, если хотите попробовать его:
Этот плагин включён по умолчанию в Create React App.
npm install eslint-plugin-react-hooks --save-dev
// Your ESLint configuration
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
"react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
}
} Вы можете перейти к следующей странице, чтобы узнать, как написать собственные хуки прямо сейчас. На этой странице мы продолжим объяснение обоснования этих правил.
Объяснение
Как мы узнали ранее, мы можем использовать несколько хуков состояния или эффекта в одном компоненте:
function Form() {
// 1. Use the name state variable
const [name, setName] = useState('Mary');
// 2. Use an effect for persisting the form
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
// 3. Use the surname state variable
const [surname, setSurname] = useState('Poppins');
// 4. Use an effect for updating the title
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});
// ...
} Так как же React понимает, какое состояние соответствует какому вызову useState? Ответ заключается в том, что React полагается на порядок вызова хуков. Наш пример работает, потому что порядок вызова хуков одинаков при каждой отрисовке:
// ------------
// First render
// ------------
useState('Mary') // 1. Initialize the name state variable with 'Mary'
useEffect(persistForm) // 2. Add an effect for persisting the form
useState('Poppins') // 3. Initialize the surname state variable with 'Poppins'
useEffect(updateTitle) // 4. Add an effect for updating the title
// -------------
// Second render
// -------------
useState('Mary') // 1. Read the name state variable (argument is ignored)
useEffect(persistForm) // 2. Replace the effect for persisting the form
useState('Poppins') // 3. Read the surname state variable (argument is ignored)
useEffect(updateTitle) // 4. Replace the effect for updating the title
// ... Пока порядок вызова хуков одинаков между отрисовками, React может сопоставить локальное состояние с каждым из них. Но что произойдёт, если мы поместим вызов хука (например, хук эффекта persistForm) внутри условного оператора?
// 🔴 We're breaking the first rule by using a Hook in a condition
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
} Условие name !== '' выполняется при первой отрисовке, поэтому мы запускаем этот хук. Однако при следующей отрисовке пользователь может очистить форму, что приведет к тому, что условие false станет ложным. Теперь, когда мы пропускаем этот хук во время отрисовки, порядок вызова хуков меняется:
useState('Mary') // 1. Read the name state variable (argument is ignored)
// useEffect(persistForm) // 🔴 This Hook was skipped!
useState('Poppins') // 🔴 2 (but was 3). Fail to read the surname state variable
useEffect(updateTitle) // 🔴 3 (but was 4). Fail to replace the effect React не будет знать, что вернуть для второго вызова хука useState. React ожидал, что второй вызов хука в этом компоненте соответствует эффекту persistForm, как и во время предыдущей отрисовки, но теперь этого нет. С этого момента каждый последующий вызов хука после пропущенного также сместится на одну позицию, что приведёт к ошибкам.
Вот почему хуки должны вызываться на верхнем уровне наших компонентов. Если мы хотим выполнить эффект условно, мы можем поместить это условие внутрь нашего хука:
useEffect(function persistForm() {
// 👍 We're not breaking the first rule anymore
if (name !== '') {
localStorage.setItem('formData', name);
}
}); Обратите внимание, что вам не нужно беспокоиться об этой проблеме, если вы используете предоставленное правило линтера eslint-plugin-react-hooks. Но теперь вы также знаете, почему хуки работают таким образом и какие проблемы предотвращает это правило.
Дальнейшие шаги
Наконец, мы готовы узнать, как создавать свои собственные хуки! Пользовательские хуки позволяют объединять хуки, предоставляемые React, в собственные абстракции и повторно использовать общую логику со состоянием между различными компонентами.
Полезная ли эта страница?
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://17.reactjs.org/docs/hooks-rules.html