Концепция кастомных хуков в React
В этом уроке разбирается структура данных «стек» и объясняется, как устроены кастомные хуки в React на простом примере. Кроме того, идет попытка "изобрести" упрощённую версию React с нуля — чтобы понять, как он работает внутри.
Что такое структура данных?
Структура данных — это способ хранения и организации данных, чтобы эффективно ими пользоваться в алгоритмах.
Разные алгоритмы требуют разные структуры данных. В этом смысле структура данных как "коробочка" для элементов — с определёнными правилами, как туда и оттуда брать данные.
Рассмотрим примеры структур данных:
- Массив — место, где данные хранятся в определённом порядке и к ним можно обратиться по индексу.
- Очередь (FIFO — первый пришёл, первый ушёл) — данные обрабатываются строго в порядке поступления, как очередь в магазине.
- Стек (LIFO — последний пришёл, первый ушёл) — данные кладутся и забираются с одного конца, пример — стопка тарелок, верхняя тарелка будет взята первой.
Что такое стек и как он связан с React
В JavaScript и React стек часто используется для отслеживания вызовов функций — call stack.
При вызове функций они "складываются" в стек: первая функция лежит внизу, последняя — сверху.
React использует стек вызовов, чтобы понять, какой компонент вызвал какой хук (например,
useState), и хранить состояние именно для этого компонента.
Как устроен React (упрощённо)
- Представим React как объект с методом
render— он получает компонент (функцию), вызывает его, получает HTML-строку и вставляет её в DOM. - Компоненты в React — это функции, которые возвращают разметку (JSX или, в упрощённом случае, строку HTML).
- Вызовы функций компонентов складываются в стек вызовов, что помогает React понять, где именно какой хук используется.
Это HTML файл, который содержит простую реализацию React-подобного рендеринга. В коде создается объект React с методом render, который принимает компонент-функцию, вызывает её и вставляет результат в элемент с id="root". Также определена функция-компонент Playlists, которая возвращает строку "I am Playlists".
Как работают хуки и кастомные хуки?
useState— базовый хук: Позволяет компоненту хранить состояние — например, число, строку. React связывает это состояние с конкретным компонентом через стек вызовов, зная точное место вызова хука.
Основные изменения:
- Добавлена функция
useState, которая принимает начальное значение и возвращает массив с этим значением и пустой функцией - В компоненте Playlists теперь используется
useState(10), из которого деструктурируетсяvalue - Компонент возвращает строку с конкатенацией значения:
'I am Playlists: ' + value
Это простая имитация React Hook useState, хотя пока без функциональности изменения состояния.
- Кастомный хук:
- Это функция, имя которой начинается с
use, и которая внутри может вызывать другие хуки (например,useState,useEffect). - Кастомные хуки не "живут" отдельно — они всегда вызываются внутри компонента, фактически добавляя дополнительные "слои" в стек вызовов.
- React при этом правильно связывает состояние и эффекты с конкретным компонентом, даже если хук вызван внутри кастомного хука.
- Добавлена новая функция
useCustomHook(), которая используетuseState(10)внутри себя и возвращаетvalue - В функции
useStateпоявилась строкаfiberNode.state = initValue;(хотя fiberNode не определен в коде) - Компонент Playlists теперь использует
useCustomHook()вместо прямого вызоваuseState
Это демонстрация композиции хуков - создание кастомного хука, который использует встроенный хук внутри себя.
Переиспользование логики: Кастомные хуки удобны для того, чтобы вынести повторяющийся код с состоянием и побочными эффектами и использовать его в разных компонентах без дублирования.
Пример с двумя счётчиками:
Создаём два компонента, использующих один и тот же кастомный хук с useState: каждый будет иметь
своё независимое состояние.
- Импортируется хук useState из React
- Компонент App рендерит два компонента Counter
- Компонент Counter использует useState для управления состоянием
- При клике на div значение увеличивается на 1
- Каждый экземпляр Counter будет иметь своё независимое состояние
Это классический пример использования React хуков и демонстрация того, как каждый экземпляр компонента имеет изолированное состояние.
React не смешивает состояния, несмотря на одинаковую функцию компонента, потому что для каждого вызова создаётся своя ячейка в памяти (FiberNode).
Custom hook useCounter инкапсулирует логику управления состоянием, компоненты Counter и
Age теперь переиспользуют эту логику вместо дублирования useState. Это делает код более
модульным и легче масштабируется при добавлении новых счётчиков.
Вынесем больше логики в наш кастомный хук:
Новая версия имеет:
- Custom hook
useCounterтеперь принимаетms(миллисекунды) для автоматического сброса значения по таймеру - Добавлен
useEffectсsetInterval- значение автоматически сбрасывается через указанный интервал - Функция
inc()для увеличения значения вместо прямогоsetValue - Компоненты используют разные параметры:
Counter(10, 4000)иAge(2, 10000)
Почему это важно?
Понимание того, как React "смотрит" на вызовы хуков через стек вызовов и хранит состояние для каждого компонента — ключ к правильному использованию хуков.
Умение создавать кастомные хуки помогает структурировать логику приложения, делает код более чистым, понятным и переиспользуемым.
Такой взгляд углубляет понимание работы React, а не просто помогает использовать его как "чёрный ящик".
🏠 Домашнее задание
Цель задания: Научиться создавать и работать с кастомными хуками
Задание 1
Описание задачи
У тебя есть готовый компонент LightSwitch с логикой переключения. Твоя задача - создать кастомный
хук useToggle и вынести в него логику управления состоянием.
Исходный код
Что нужно сделать
- Создай кастомный хук
useToggle, который:
- Принимает начальное значение
initialValueтипаboolean - Возвращает объект с двумя свойствами:
isOnиtoggle
-
Перенеси логику из компонента
LightSwitchв хукuseToggle -
Используй созданный хук в компоненте
LightSwitch
Итоговый результат 🚀

Задание 2
Описание задачи
Теперь расширь функциональность хука useToggle и используй его в нескольких компонентах.
1. Добавь в хук setIsOn
Хук useToggle теперь должен возвращать объект с тремя свойствами:
isOn- текущее булево состояниеtoggle- функция для переключения состоянияsetIsOn- функция для установки конкретного значения
2. Создай компонент VisibilityToggle
Компонент должен:
- Использовать хук
useToggleс начальным значениемfalse - Показывать/скрывать секретное сообщение "🎉 Это секретное сообщение!"
- Иметь две кнопки: "Показать" и "Скрыть"
3. Добавь компонент на страницу
Добавь VisibilityToggle в компонент TogglePage рядом с LightSwitch.
Пример структуры
Итоговый результат 🚀

Задание 3
Описание задачи
Дорабатай хук useToggle, добавив функцию сброса состояния к начальному значению.
1. Добавь метод reset в хук useToggle
Хук теперь должен возвращать объект с четырьмя свойствами:
isOn- текущее булево состояниеtoggle- функция для переключения состоянияsetIsOn- функция для установки конкретного значенияreset- функция для сброса состояния к начальному значению
2. Создай компонент NotificationSwitch
Компонент должен:
- Использовать хук
useToggleс начальным значениемtrue - Отображать текст "🔔 Уведомления включены" или "🔕 Уведомления выключены"
- Иметь три кнопки:
- "Переключить" - переключает состояние
- "Включить" - устанавливает
true - "Сбросить по умолчанию" - возвращает к начальному состоянию
3. Добавь компонент на страницу
Добавь NotificationSwitch в компонент TogglePage.
Пример структуры
Итоговый результат 🚀

Задание 4
Описание задачи
Создай кастомный хук useText для работы с текстовыми строками. Этот хук должен управлять текстом и
предоставлять методы для его изменения.
1. Создай кастомный хук useText
Хук должен:
- Принимать один параметр:
initialText- начальное значение текста (по умолчанию пустая строка"")
- Возвращать объект с пятью свойствами:
text- текущий текстsetText- функция для установки нового текстаclear- функция для очистки текста (устанавливает пустую строку)toUpperCase- функция для преобразования текста в ВЕРХНИЙ РЕГИСТРtoLowerCase- функция для преобразования текста в нижний регистр
2. Создай компонент TitleEditor
Компонент должен:
- Использовать хук
useTextс начальным значением"Заголовок статьи" - Отображать текущий текст в заголовке
<h2> - Иметь четыре кнопки:
- "ВЕРХНИЙ РЕГИСТР" - преобразует текст
- "нижний регистр" - преобразует текст
- "Изменить на 'Новый заголовок'" - устанавливает новый текст
- "Очистить" - очищает текст
3. Создай компонент GreetingCard
Компонент должен:
- Использовать хук
useTextс начальным значением"Привет!" - Отображать текст внутри div с эмодзи:
"💬 {text}" - Иметь четыре кнопки:
- "ГРОМКО" - преобразует в верхний регистр
- "тихо" - преобразует в нижний регистр
- "Сказать 'Добро пожаловать!'" - устанавливает новый текст
- "Молчать" - очищает текст
4. Создай страницу TextPage
Компонент TextPage должен отображать оба компонента.
Подсказки
- Используй
useStateдля хранения текущего текста - Методы
toUpperCaseиtoLowerCase- это встроенные методы строк в JavaScript - Все функции должны изменять состояние через
setState
Пример структуры
Итоговый результат 🚀

🔶 Помни: кастомные хуки — это не усложнение, а упрощение будущей жизни. Когда логика повторяется в третий раз (а она повторится), ты правишь код в одном месте, а не ищешь одинаковые useState по всему проекту. Сегодняшние 10 минут на создание хука экономят завтрашние часы копипасты.
🔶 Код-ревью скажет спасибо. Когда ты пишешь useToggle вместо копирования 10 строк логики в каждый компонент, твой код становится читаемым как книга. Коллега откроет файл и сразу поймёт: "Ага, здесь используется переключатель". Не нужно разбираться в деталях — название хука всё объясняет. 📖


