Как сделать очень длинный хук

Обновлено: 07.07.2024

Как создавать собственные хуки на React

Хуки React, впервые появившиеся в версии React v16.8, кардинально изменили подход к написанию кода. Из коробки React предлагает пользователю доступ к ряду разнообразных хуков, таких как useState , useEffect , useReducer и многим другим, однако имеется возможность создавать и свои варианты, применяя более сложную логику состояний.

В процессе написания React-приложений часто происходит так, что одна и та же повторяющая и избыточная логика состояний из раза в раз кочует между несколькими компонентами. С помощью собственных хуков можно разделить эту логику на функции, чтобы сделать код более читабельным и многоразовым.

Пользовательские хуки являются функциями, охватывающими другие хуки и содержащими общую логику состояний, которую можно вновь использовать в других компонентах. Такие функции закреплены за префиксным словом use . Они позволяют делать код короче и чище.

Прежде чем писать собственный React-хук, следует иметь в виду, что сообщество React уже опубликовало тысячи разных вариантов. Велика вероятность того, что хук с нужной вам логикой уже был написан и опубликован в сети. Однако эта статья не о том, следует ли писать хуки, а о том, как это делать.

useLocalStorageState

Посмотрим на этот компонент Counter , который хранит текущее значение count в локальном хранилище:

В этом примере мы используем useState и useEffect , чтобы синхронизировать локальное состояние с локальным хранилищем. А что делать в том случае, если нужно дублировать логику этого фрагмента в другие компоненты? Вместо того, чтобы копировать и вставлять код, можно создать новый пользовательский хук для обработки этой логики.

useLocalStorageState принимает два параметра: ключ и значение по умолчанию. При первой инициализации программа принимает счётчик count из локального хранилища, если он существует, и задаёт ему значение состояния. В противном случае она возвращает значение по умолчанию. Затем мы используем хук useEffect , благодаря которому локальное хранилище синхронизируется с локальным состоянием, и возвращаем функции state и setState как массив. Обратите внимание, что мы преобразуем значения в строку и парсим их из localStorage , используя JSON.stringify и JSON.parse . Это значит, что с помощью этого хука мы можем хранить в том числе и объекты.

Теперь можно использовать хук useLocalStorageState в любом доступном компоненте.

useArray

Обратите внимание, что пользовательский хук не всегда должен возвращать именно массив. В следующем примере мы создадим собственный хук useArray , который упростит управление состояниями массива.

Этот хук выглядит довольно просто. Он возвращает объект со множеством модификаторов для состояния массива, с помощью которого можно легко им манипулировать. Мы берём исходный массив в качестве аргумента хука, затем добавляем add , clear , removeById и removeIndex как дополнительные функции помимо обычных значений и операций setValue .

А вот как можно реализовать этот хук внутри компонента:

Здесь видно, как благодаря использованию пользовательского хука мы значительно уменьшили количество посторонней логики, которая присутствовала бы в этом компоненте.

Обязательно ли ставить use в начале пользовательских хуков

Как гласит документация React:

Пользовательские хуки не копируют состояние

Каждый вызов такого хука включает собственное состояние, поэтому не получится автоматически задать ему текущее. Однако если связать пользовательский хук с библиотекой глобальных состояний вроде Redux или Recoil, то можно создавать варианты, которые смогут взаимодействовать с глобальным состоянием.

Заключение

Пользовательские хуки React — очень мощный инструмент для написания более чистого, читабельного и поддерживаемого кода. Мы рассмотрели несколько наглядных примеров таких хуков, а именно useLocalStorageState и useArray , и узнали, как можно использовать их для снижения сложности кода и повышения удобства повторного использования.


Пользовательские хуки React – чрезвычайно полезный инструмент, который позволяет добавлять особые возможности и уникальные функции в ваши компоненты и приложения React.

В большинстве случаев, если вы хотите добавить дополнительную функциональность в свое приложение, вы можете просто установить стороннюю библиотеку, которая предназначена для решения определенной задачи. Но если такой библиотеки или хука не существует, что делать?

Поэтому разработчику, использующему React, для того, чтобы добавить недостающий функционал в свои проекты, важно детально изучить процесс создания пользовательских хуков. Этот механизм, реализованный в ядре библиотеки React, предоставляет оптимальный подход к решению различных задач, не поддерживаемых стандартными средствами этой библиотеки.

В этом пошаговом руководстве я покажу вам, как создать свои собственные хуки React, разобрав подробно примеры хуков, которые я разработал для своих приложений. Также я опишу саму проблему и формальную постановку задачи, для решения которых эти хуки были разработаны.

Хотите узнать, как создавать собственные хуки React при создании крутых реальных приложений? Переходите по ссылке React Bootcamp .

1. Хук useCopyToClipboard

Разберемся, как это работает. Пользователь наводит указатель мыши на блок с фрагментом кода, нажимает на кнопку для отправки в буфер обмена, и его содержимое добавляется в буфер обмена компьютера. В дальнейшем пользователь может вставить скопированный код в любой редактор или использовать по своему усмотрению.

Однако вместо того, чтобы использовать стороннюю библиотеку, я захотел воссоздать эту функциональность с помощью моего собственного хука React. Как и в случае с любым создаваемым хуком, я помещаю файл с его кодом в специальную папку, обычно называемую utils или lib . Далее она используется для хранения файлов с разработанными мною функциями, которые я повторно использую в своем проекте.

Код с новым хуком будет находится в файле useCopyToClipboard.js, в который я также помещу функцию с тем же именем.

Существует несколько различных способов реализовать копирование текста в буфер обмена пользователя. И ранее для этого, я обычно предпочитал использовать проверенную библиотеку из пакета copy-to-clipboard , что делает процесс разработки более быстрым и надежным.

В следующем примере кода мы экспортируем из пакета функцию copy , которую далее при необходимости будем вызывать в своем коде.

Теперь создадим новую функцию, которая будет использоваться для копирования текстового содержимого, которое будем добавлять в буфер обмена пользователя, и назовем ее handleCopy .

Разрабатываем функцию handleCopy

После проверки типа, полученное содержимое и преобразуем в строку, которую передаем в импортируемую функцию copy . Затем мы возвращаем функцию handleCopy из хука в любое место нашего приложения.

Функция handleCopy будет связана с обработчиком onClick для соответствующих кнопок.

Но кроме того, нам нужно сохранять состояние компонента, которое будет содержать информацию, был ли скопирован текст или нет. Чтобы реализовать это, в верхней части нашего хука мы будем вызывать метод useState , и таким образом создадим новую переменную состояния isCopied , для изменения значения которой будет вызываться функция-сеттер setCopy .

Начальное значение переменной isCopied будет задано логическим значением false . Если текст был успешно скопирован, то после вызова функции copy установим значение isCopied равным true . В противном случае – false .

И наконец, мы будем возвращать из функции хука массив, содержащий переменную состояния isCopied вместе с функцией handleCopy .

Отлично, теперь мы можем использовать хук useCopyToClipboard в любом компоненте, где нам это необходимо.

В моем случае я буду использовать его с компонентом кнопки копирования в буфер обмена, который получает содержимое снипета кода из специального блока на странице.

Чтобы этот код заработал необходимо добавить обработчик клика мыши для нужной кнопки. При клике на кнопке, содержимое блока в текстовом формате будет передано в буфер обмена. При успешном копировании, переменной isCopied будет передано значение true . Соответственно мы можем показать другой значок иконки, обозначающий, что операция копирования была успешна.

Добавляем интервал сброса состояния кнопки копирования

Как мы уже говорили, после успешного копирования текста с помощью нашего хука значение переменной isCopied становится истинным (и таким впоследствии остается). Это означает, что мы и далее будем постоянно видеть значок успешного копирования.

Если мы захотим сбрасывать состояние кнопки копирования через определенное количество секунд в исходное состояние, то можем передать соответствующий временной интервал в хук useCopyToClipboard . Давайте добавим ему эту полезную функциональность.

Возвращаясь к коду, добавим для функции вызова хука соответствующий параметр resetInterval , с начальным значением null , это гарантирует, что состояние компонента кнопки по умолчанию не будет сбрасываться.

Добавим стандартный хук useEffect , в который передадим значение интервала сброса состояния resetInterval и переменную isCopied . После успешного копирования и передачи соответствующего значения переменной isCopied , мы запускаем функцию-таймер setTimeout с временем срабатывания равным интервалу сброса, после чего возвращаем переменной isCopied значение false .

И, наконец, мы можем сделать последнее усовершенствование: обернуть функцию handleCopy в хук useCallback для того , чтобы убедиться , что он не будет создаваться заново каждый раз, когда наш компонент повторно рендерится.

И так теперь у нас есть финальная версия кода нашего хука, который теперь позволяет сбрасывать свое состояние через заданный интервал времени. Если мы передадим ему определенное значение времени, то должны увидеть результат, подобный приведенному ниже.

2. Хук usePageBottom

В приложениях React в некоторых случаях важно знать, когда пользователь прокрутил страницу до конца.

Как и ранее, начнем с создания в нашей папке utils отдельного файла usePageBottom.js в который добавим функцию (хук) с тем же именем:

И так, нам необходимо определить момент, когда пользователь попадет в нижнюю часть страницы. Мы можем определить его произведя несложные расчеты, используя информацию, полученную из свойств глобального объекта window . Отметим, что перед тем как обращаться к нему, нам нужно убедиться, что компонент, в котором вызывается хук, смонтирован. Для этого мы будем использовать хук useEffect , в который передадим пустой массив зависимостей.

Пользователь будет прокручивать страницу до конца, то есть до тех пор пока значение свойства window.innerHeight плюс значение свойства document.scrollTop не станет равным document.offsetHeight . Таким образом, если результат проверки этого условия будет истинным, то будем считать пользователь прокрутил страницу до конца:

Сохраним результат этого выражения в переменной isBottom , а затем обновим переменную состояния bottom , которую мы в конечном итоге вернем из нашего хука.

Вот так будет в результате выглядеть наш код. Однако в таком виде он работать не будет. Давайте разберемся почему так происходит.

И наконец, поскольку у нас устанавливается обработчик события скролла окна браузера, то нужно предусмотреть ситуацию, когда пользователь уходит со страницы или же наш компонент удаляется. В этом случае необходимо удалять уже не нужный обработчик событий прокрутки, чтобы его код не попытался обновить переменную состояния компонента, которая больше не существует, что приведет к появлению ошибки.

Разрешить эту проблему можно возвращая из хука useEffect функцию, в которую обернем метод window.removeEventListener и передадим ему ссылку на функцию handleScroll .

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

3. Хук useWindowSize

На моем сайте, реализованном на Gatsby, по мере уменьшения ширины страницы я хочу управлять отображением ссылок в меню, которое находится в верхнем header блоке.

Для этого мы могли бы в файле с разметкой JSX использовать медиа-запрос CSS или пользовательский хук React, предоставляемый сторонней библиотекой, чтобы в зависимости от текущего размера страницы скрывать или показывать ее элементы.

Раньше я использовал хук, импортируемый из библиотеки react-use , но вместо того, чтобы каждый раз подключать целую библиотеку, я решил создать свой собственный. Этот хук, который я назвал useWindowSize , при необходимости вычислял бы размеры окна, то есть его ширину и высоту и возвращал эти значения в нужном месте кода.

Разработка хука

Для начала в нашей папке utils создадим новый js файл с тем же именем, что и название хука useWindowSize . Затем я импортирую React, для того чтобы использовать стандартные хуки в коде нашего хука и его корректного экспорта.

И так, поскольку я хочу использовать наш хук на сайте, работающем на Gatsby с серверным рендерингом страниц, то мне нужно как-то получить размер окна браузера пользователя. Но у нас нет и не может быть непосредственно к нему доступа, потому что мы осуществляем рендер страниц в конечном виде на сервере.

То есть для корректной работы хука нам необходимо предусмотреть тот факт, что наш код может работает как в браузере, так и на сервере. Для его определения мы можем проверить соответствие результата выполнения выражения typeof window и строки undefined .

В случае для браузера по умолчанию, мы можем возвращать из функции хука объект, содержащий свойства с фиксированными значениями ширины и высоты, допустим, 1200 и 800 пикселей:

Как получить ширину и высоту окна

И так, если наш код выполняется у пользователя в браузере и мы можем непосредственно получить ссылку на объект window , то воспользуемся стандартным хуком useEffect . В этом случае мы можем использовать его побочный эффект и взаимодействовать с объектом window напрямую. Включим пустой массив в качестве зависимостей для его выполнения, чтобы гарантировать, что функция эффекта будет вызывается только один раз после того, как компонент (в котором вызывается хук) будет смонтирован.

При изменении размера окна будет вызвана функция обратного вызова, и состояние компонента, а точнее значение переменной windowSize будет обновлено с учетом текущих размеров окна браузера. Для этого используются значения ширины и высоты из объекта window.innerWidth и window.innerHeight .

Как добавить поддержку SSR (серверного рендеринга)

Тем не менее с использованием технологии SSR, код в том виде работать не будет. Это связано с тем, что ключевое правило хуков состоит в том, что их нельзя вызывать при необходимости в зависимости от выполнения каких-либо условий. Как результат этого ограничения, мы не можем для их проверки использовать условные операторы до вызова хуков useState , либо useEffect .

Далее мы будем использовать тернарный оператор для установки значений ширины и высоты, предварительно проверив, находимся ли мы на сервере. Если это так, мы будем использовать значение по умолчанию, а если нет, мы будем использовать значения из объекта window.innerWidth и window.innerHeight .

И наконец, в конце кода хука вернем значение переменной состояния windowSize .

Чтобы использовать наш хук, нужно импортировать его, а затем при необходимости вызывать и использовать полученные значения размеров окна по своему усмотрению. Например, в зависимости от ширины окна скрывать или показывать определенные элементы страницы.

Этот хук будет также корректно работать с любым серверным приложением React, таким как Gatsby и Next.js.

4. Хук useDeviceDetect

Я создал лендинг для продвижения своего нового курса, и у меня возникла очень странная ошибка на мобильных устройствах. На настольных компьютерах стили сайта выглядели великолепно. Но когда я посмотрел на мобильную версию, то увидел, что всё сломалось и сайт выглядел совсем не так, как я задумал.

Я отследил причину проблемы до одной сторонней библиотеки под названием response-device-detect , которую я использовал, чтобы определять, использует ли пользователь для просмотра сайта мобильное устройство или нет. И если это так, я бы убирал слишком большой заголовок из-за которого ломалась страница.

Проблема заключалась в том, что эта библиотека не поддерживает рендеринг страниц на стороне сервера, который Gatsby использует по умолчанию. Поэтому мне пришлось создать собственное решение, для проверки, когда пользователь пользуется мобильным устройством. И для этого я написал хук, который назвал useDeviceDetect .

Работа над кодом хука

Как и ранее я создал в моей папке utils отдельный файл с тем же именем, useDeviceDetect.js. И поскольку хуки – это просто функции JavaScript для общего назначения, которые используются механизмом хуков в React, я создаю функцию с тем же именем useDeviceDetect и импортирую React.

Как получить информацию о пользовательском агенте user agent

Способ, которым мы воспользуемся, чтобы определить тип устройства, которое использует пользователь – это получить и проанализировать значение свойства userAgent , которое в свою очередь содержится в свойстве navigator глобального объекта окна window .

А поскольку взаимодействие с интерфейсом окна браузера window API, как и с API внешнего ресурса можно в общем случае рассматривать как побочный эффект, то мы можем получить доступ к информации о пользовательском агенте внутри кода хука useEffect .

После монтирования компонента используем инструкцию кода typeof navigator , чтобы определить, выполняется код в браузере или же на сервере. Если мы находимся на сервере, то у нас соответственно не будет доступа к объекту window . Поэтому результат выполнения выражения typeof navigator будет эквивалентен строке undefined , поскольку этого свойства не существует. В противном случае (код выполняется в браузере) мы можем получить значение свойства, содержащего информацию о пользовательском агенте, способом описанном выше.

Используем тернарный оператор для получения данных userAgent следующим образом:

Как определить, соответствует ли значение userAgent мобильному устройству

Значение свойства userAgent представляет собой строку, которая содержит следующие имена устройств, соответствующие мобильным устройствам: Android, BlackBerry, iPhone, iPad, iPod, Opera Mini, IEMobile или WPDesktop.

И всё, что нам нужно сделать это, используя полученную строку и метод match() с соответствующим регулярным выражением, проверить содержит ли она одну из перечисленных строк. Логическое значение, полученное в результате выполнения проверки сохраним в локальной переменной с именем mobile .

Далее сохраним полученный результат в состоянии компонента, используя хук useState , которому при вызове присвоим начальное значение false . Для этого создадим переменную состояния isMobile , и соответствующую ей функцию-сеттер setMobile .

Теперь, как только в переменную mobile будет передаваться значение мы соответственно будем изменять состояние компонента. И наконец, мы вернем из хука объект, чтобы в дальнейшем добавить в него больше свойств, если захотим привнести большей функциональности нашему хуку.

В возвращаемый объект добавим значение isMobile :

Вернемся к нашему лендингу. Теперь, используя синтаксис деструктуризации объекта, мы можем вызывать наш хук в любом месте кода, чтобы получить значение, соответствующее типу устройства:

Заключение

В своей статье каждым из рассмотренных примеров я попытался проиллюстрировать возможности, которые дают пользовательские хуки React для решения практических задач, когда сторонние библиотеки терпят неудачу.

Я надеюсь, что это руководство дало вам лучшее представление о том, когда и как необходимо разрабатывать свои собственные хуки React. Не стесняйтесь использовать любой из хуков, рассмотренных в этой статье и приведенный их код в своих проектах, а также в качестве источника вдохновения для написания кода ваших хуков React.

Один из эффективных ударов, применяемый практически во всех единоборствах. По статистике, в боксе большинство нокаутов случаются после хука, нанесённого передней рукой. Рассмотрим технику удара подробно. Для наглядности предположим, что мы работаем в левосторонней стойке.

Хук с разворотом кулака. В боксе часто учат пробивать хук, разворачивая кулак и предплечье параллельно полу (см. фото и видео). Наносить таким образом боковой удар без боксёрских перчаток я не рекомендую. Мощный хук при таком положении руки легко повредит запястье из-за большой неравномерной нагрузки на сустав. Нанося хук без перчаток (или в перчатках для ММА или РБ), разворачивайте кулак таким образом, чтобы плюсневелая косточка среднего пальца была выше косточки указательного, а локоть бьющей руки выше кулака. Такой хук даже при сильном ударе безопасен для вашей руки (о подготовке ударных поверхностей смотрите соответствующий раздел).

Классическое положение руки при нанесении хука
Классическое положение руки при нанесении хука. При работе без боксёрских перчаток не рекомендуется
Безопасное положение руки при нанесении хука с доворотом кулака
Безопасное положение руки при нанесении хука с доворотом кулака
При атаке с дистанции чаще используют хук с доворотом кулака
При атаке с дистанции чаще используют хук с доворотом кулака

Боковой удар (хук, крюк) рукой на сайд-степе. Крайне эффективный приём для встречной атаки соперника. При атаке, например, прямым ударом, левую ногу ставим влево, носком внутрь. Корпус разворачиваем по часовой стрелке, смещаясь с линии атаки влево. Одновременно выбрасываем левый хук. Эффективным продолжением будет подтянуть правую ногу назад к положению стойки и нанести правый прямой.

Уход с контратакой после бокового удара на сайдстепе. Встретив противника хуком на сайдстепе, выполните уход правой ногой на 90 градусов по часовой стрелке. После этого проведите контратаку. Приём несложный, отлично работает, когда противник ведёт бой агрессивно и активно наступает.

Следующий урок:

Прямой удар ногой
Очень эффективный приём в спортивном и особенно в реальном рукопашном бою. В спортивном поединке бойцы обычно работают без обуви и попадания в локоть соперника не очень приятны. В реальном бою стопа защищена ботинком и эффективность прямого удара ногой повышается в разы.


Хук (в переводе с английского – крюк) – это боковой удар, один из сильнейших и коварных ударов в боксе. Он наносится не по прямой линии, а по изогнутой траектории напоминающей крюк. При этом хук выпадает из поля зрения соперника, и защищаться ему приходится наугад. Вот поэтому то частым следствием удачно выполненного хука является нокаут. Именно не замеченный, сильный удар и способен вывести соперника из равновесия, ошеломить его, перехватить инициативу, а при удачной ситуации привести к досрочному завершению встречи. Опасность бокового удара в его непредсказуемости и мощности, которые в буквальном смысле слова ошеломляют и выводят из строя. Опытные бойцы в ближнем бою пробивают его с молниеносной скоростью.

Виды хука и техника исполнения

При прямых ударах вес тела подается вперёд. При коротком же хуке, удар осуществляется с места, вкручивая тело. Силу удару придаёт вращающий момент, а скорость – работа косых мышц живота. Различают длинные и короткие боковые удары.

Длинный хук справа начинают с толчка правой ноги, потом следует разворот корпуса, плечевого пояса и рука посылается вперёд. Локоть идёт параллельно земле, угол между предплечьем и плечом боксёра тупой. Если локоть опустить ниже, то открывается лицо, и можно получить удар в голову от противника.

Длинный левый хук выполняют после подготовительных действий (уклона влево или джеба и уклона), что даёт возможность перенести вес тела с правой ноги на левую ногу.

Хук – очень сильный удар . И его стараются бить наверняка. На силу бокового удара влияет как скорость нанесения его боксёром, так и радиус вращения тела. Когда локоть держат параллельно земле, то радиус максимальный. Если локоть поднят слишком высоко или опущен слишком низко, то радиус уменьшается, что снижает силу удара.

Короткие хуки напоминают расстрел в упор. Бьют их, как правило, по раскрытому, загнанному в угол сопернику. При коротком боковом ударе вращение тела осуществляется сильнее, чем при длинном хуке.

Короткий хук справа начинается с толчка ногой, потом поворачивается корпус, далее резко и сильно вворачивается плечевой пояс и выносится рука.

Короткий боковой слева начинают с манёвра (уклона или нырка), при этом переносится вес тела с правой ноги на левую. Далее следует толчок левой ступнёй, разворот и нанесение удара. При коротких хуках, как справа, так и слева локоть идёт параллельно земле, угол между предплечьем и плечом прямой.

Сила удара связанна с тем, что в него включаются одновременно руки, туловище и ноги. Вообще этот принцип нужно усвоить любому начинающему боксёру. Если в удар не вкладывать все эти составляющие, то он попросту не будет иметь силы. И конечно же, нужно как можно скорее осваивать хук в боксе и учиться применять его на практике, при спарринге. Потому что спортсмены, владеющие им в совершенстве – это очень опасные соперники. У русского народа издавна в кулачных боях применялось слово крюк. Англоязычный аналог этого слова вошёл в обиход сравнительно недавно.

Хук в боксе является грозным оружием спортсменов, ориентированных на атакующую технику ведения боя, заканчивающих встречу нокаутом (Майк Тайсон).

Читайте также: