Понимание пользовательских хуков React: повторное использование логики без общего состояния
Узнайте, что такое пользовательские хуки React, как они извлекают и обмениваются логикой с состоянием между компонентами, и каких распространённых ошибок следует избегать при их создании.
Введение
Бывало ли у вас так, что вы писали один и тот же набор функций useState и useEffect в трех разных компонентах, меняя лишь имена переменных? Почти каждый разработчик React сталкивается с этой проблемой. Код работает нормально, но это приводит к дублированию идентичной логики во всем проекте, причем каждая копия со временем начинает отклоняться из-за независимых изменений.
Именно такая ситуация и предназначены для решения пользовательские хуки в React. документация React по этой теме объясняет, что вынесение поведения компонента в отдельную функцию, которую можно использовать многократно, позволяет разным частям приложения делиться одинаковым состоянием, вместо того чтобы постоянно копировать основную реализацию. В этой части статьи рассматривается, что такое пользовательские хуки на самом деле, как они работают в реальном коде, а также преимущества и недостатки, которые необходимо понимать перед созданием собственных хуков.
Что такое пользовательские хуки в React
По сути, пользовательский хук — это просто функция на JavaScript, которая подчиняется одному строгому правилу именования: её имя должно начинаться с use. Внутри неё можно вызывать другие хуки, такие как useState или useEffect, и возвращать те значения или функции, которые нужны компоненту-запросчику. Сам React не предоставляет этой функции никакого особенного обработчика во время выполнения — префикс use является чисто конвенциональным, но он крайне важен, поскольку позволяет React правильно соблюдать правила хуков и помогает другим разработчикам мгновенно понять, для чего предназначена функция.
Пользовательские хуки против обычных функций
Стандартная вспомогательная функция может выполнять множество полезных задач, но у неё нет возможности вызывать другие хуки, поскольку она находится вне механизма отрисовки React. У пользовательского хука нет таких ограничений. Именно в этом его преимущество: вы можете объединить поведение, связанное со состоянием — отслеживание флага загрузки, прослушивание событий браузера или что-то подобное — во что-то переиспользуемое, что при этом будет корректно интегрироваться в жизненный цикл компонентов React.
Как на практике работают пользовательские хуки React
Конкретный пример проясняет ситуацию гораздо быстрее, чем любое абстрактное объяснение.
Выделение логики из компонента
Представьте три не связанных между собой компонента, каждый из которых должен определять, имеет ли браузер в настоящее время сетевое подключение. Без общего хука каждому из них придётся создавать собственные настройки useState и useEffect для отслеживания этого состояния:
function StatusBadge() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const goOnline = () => setIsOnline(true);
const goOffline = () => setIsOnline(false);
window.addEventListener('online', goOnline);
window.addEventListener('offline', goOffline);
return () => {
window.removeEventListener('online', goOnline);
window.removeEventListener('offline', goOffline);
};
}, []);
return <span>{isOnline ? 'Online' : 'Offline'}</span>;
}
Вместо этого вы можете перенести всю эту логику в отдельный пользовательский хук, и с тех пор любой компонент, который нуждается в статусе, просто будет вызывать его:
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const goOnline = () => setIsOnline(true);
const goOffline = () => setIsOnline(false);
window.addEventListener('online', goOnline);
window.addEventListener('offline', goOffline);
return () => {
window.removeEventListener('online', goOnline);
window.removeEventListener('offline', goOffline);
};
}, []);
return isOnline;
}
// Usage in any component:
function StatusBadge() {
const isOnline = useOnlineStatus();
return <span>{isOnline ? 'Online' : 'Offline'}</span>;
}
Теперь ни одному из компонентов не нужно самостоятельно настраивать обработчики событий — каждый просто запрашивает у useOnlineStatus текущее значение.
Обмен логикой состояния без обмена самим состоянием
Этот момент часто становится неожиданностью для новичков: пользовательский хук делится поведением, а не самим состоянием. Если два отдельных компонента вызывают useOnlineStatus, каждый из них получает свою собственную закрытую копию этого состояния. Они выполняют одинаковую логику, но фактические значения внутри каждого компонента полностью независимы — между ними нет общего или связанного состояния.
Основные преимущества использования пользовательских хуков в React
- Возможность повторного использования в разных компонентах: Как только логика помещается в хук, ни одному компоненту приложения не нужно дублировать ни одну строку кода для её использования, что гарантирует единообразие поведения во всех местах применения.
- Более чистые и удобные для чтения компоненты: Выделение логики, связанной со состоянием, из компонента означает, что оставшийся код компонента практически не содержит проблем, связанных с управлением состоянием, что значительно упрощает его понимание новичками с первого взгляда.
- Упрощённое тестирование в изоляции: Выделенную логику можно тестировать полностью отдельно от любых компонентов, которые её используют, что позволяет проверять её лишь один раз, вместо того чтобы каждый раз перетестировывать её косвенно при изменении компонентов-использователей.
use — требует настоящего понимания основных подходов React. Команды, желающие правильно решить эту задачу с самого начала, иногда обращаются за помощью к специалистам вне своего штата, привлекая профессиональных разработчиков ReactJS, чей опыт помогает сохранять надёжность дизайна хуков по мере расширения проекта.Распространённые ошибки, которых следует избегать
Даже хуки, написанные с хорошими намерениями, могут дать сбой несколькими предсказуемыми способами.
- Нарушение правил использования хуков: Вызов хука внутри условия, в цикле или из обычной функции вместо компонента или другого хука мешает React поддерживать целостность состояния при каждом отрисовывании. В результате обычно возникают странные, периодические ошибки, которые трудно отследить до их причины.
- Чрезмерная нагрузка на один хук: Собирание в один пользовательский хук функций проверки форм, сетевых запросов и управления состоянием интерфейса затрудняет его тестирование, повторное использование и понимание по сравнению с ситуацией, когда эти же задачи распределены между двумя-тремя более мелкими, специализированными хуками.
useState заслуживает того, чтобы стать отдельным хуком. Если какой-то фрагмент логики используется только в одном месте, его извлечение приводит лишь к дополнительному уровню косвенности без реальной пользы.Заключение
Пользовательские хуки в React не только помогают избегать повторения кода, но и служат реальным механизмом для поддержания целостности и согласованности постоянно расширяющейся базы кода. Как только команда развивает способность распознавать логику, которую стоит вынести в отдельный хук, компоненты становятся менее сложными, баги легче находить, а поведение приложения остается единым во всем его объеме, вместо того чтобы постепенно отклоняться у разных компонентов, которые должны были работать одинаково. По мере того как приложения на React становятся все более масштабными и сложными, понимание того, когда и как создавать надежный пользовательский хук, превращается в базовую навык, а не во что-то, предназначенное только для опытных разработчиков.
Если ваша команда сталкивается с кодовой базой, полной дублирующейся логики, и хочет внедрить в неё настоящую структуру, возможно, имеет смысл обратиться к компании по разработке на React js, которая уже руководила подобными процессами оптимизации на других крупномасштабных проектах.
Связанные статьи
- О типизации React Hooks: useState, useEffect, useReducer и пользовательские хуки — Узнайте, как правильно типизировать useState, useEffect, useReducer и пользовательские хуки в TypeScript, а также когда использование TypeScript вместо обычного JavaScript действительно оправдано.