Как сделать свой хук react

Добавил пользователь Владимир З.
Обновлено: 06.09.2024

Узнай, как Hooks может помочь тебе создать React приложение.

Hooks - это новый функционал, который позволяет использовать state и другие функции React в компоненте без использования классов.

До появления Hooks, некоторые ключевые вещи в компонентах были возможны только с использованием классовых компонентов: наличие собственного state и использование событий жизненного цикла. Но теперь, с помощью Hooks, мы можем воспроизвести аналогичное поведение в функциональных компонентах.

Hooks - это новые дополнения, которые доступны в React 16.8.

Как использовать useState Hook

Прежде, мы использовали state для хранения данных, которые потом использовались в нашем приложении. С помощью классов, state определяется подобным образом:

Прежде всего, для того что бы использовать state в функциональном компоненте, нужно импортировать useState из React. Затем, для создания компонента, мы используем функцию, а не класс.

useState() API возвращает массив, первое значение содержит state (мы назвали его count , но его можно назвать как угодно) и второе это функция, которую мы вызываем для изменения состояния.

useState() принимает в качестве аргумента начальное значение state . В примере это 0 .

Так как useState() возвращает массив, мы используем деструктуризацию массива для доступа к каждому отдельному элементу, например: const [count, setCount] = useState(0) , где:

count - это текущий state , а setCount это функция которая меняет state .

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

Пример на codesandbox:

Как использовать useEffect Hook

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

В классовых компонентах, мы часто делаем side effect функции. Например, подписываемся на события или делаем запросы на получение данных, используя методы componentDidMount , componentWillUnmount и componentDidUpdate .

Hooks предоставляет useEffect() API, который принимает функцию в качестве аргумента.

Функция запускается при первом рендеринге компонента и при каждом последующем повторном рендеринге / обновлении. Сначала React обновляет DOM, а затем вызывает любую функцию, переданную в useEffect() . И всё это без блокировки рендеринга пользовательского интерфейса, даже при блокировке кода, в отличии от старых componentDidMount и componentDidUpdate . Что позволяет приложениям работать быстрее.

componentWillUnmount может быть достигнута путем возвращения функции из useEffect() :

Функция useEffect() может вызываться много раз, что удобно для разделения несвязанной логики.

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

Точно так же можно сказать React выполнить side effect только один раз (во время сборки и разборки), передав пустой массив:

useEffect() отлично подходит для доступа к сторонним API и многого другого.

Пример на codesandbox:

Hooks - это функции JavaScript, но при их использовании необходимо следовать правилам. Также есть плагин линтера для автоматического применения этих правил:

  • Используй Hooks только на верхнем уровне. Не вызывай Hooks внутри циклов, условий или вложенных функций.
  • Используй Hooks только из функций React. Не вызывай Hooks из обычных функций JavaScript.

или в package.json файле добавь:

Следующие hooks необходимы только для конкретных крайних случаев.

  • useReducer
  • custom hook
  • useMemo
  • useCallback

В качестве альтернативы useState ты можешь использовать хук useReducer .

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

В качестве аргументов он принимает reducer типа (state, action) => newState , начальное состояние или initialState и возвращает текущий state в паре с методом dispatch . (Если ты знаком с Redux, тогда ты уже знаешь, как это работает.)

Вот, пример как можно заменить setCount и setName , на useReducer :

При нажатии на кнопку Increment мы делаем dispatch действия < type: "INCREMENT" >, а внутри reducer , в зависимости от action.type , возвращает разный state .

Пользовательский хук или Custom hook

Распространенным способом совместного использования кода между компонентами является Render Prop Components или Higher Order Components (HOC), но с помощью Hooks всё гораздо проще. Ты можешь взять код из существующего компонента и извлечь логику хука для повторного использования.

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

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

useMemo возвращает запомненное значение.

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

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

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

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

Мы можем немного модифицировать наш пользовательский хук useState и добавить localStorage чтобы после перезагрузки страницы значения не обнулялиcь.

Для этого в useState , для начала, мы создадим функцию initialValue в которой читаем значения из localStorage по ключу “state” и возвращаем его. Если значения по ключу нет, мы берем значение из defaultValue . :

Потом передаем initialValue в useReducer вторым аргументом. Однако, useReducer , в отличие от useState , не поддерживает передачу функции в качестве опции. Вместо этого мы используем хук useMemo . Второй параметр в useMemo указывает, когда запомненная версия должна измениться. В нашем случае мы хотим, чтобы оно всегда было одинаковым, поэтому передаем пустой массив.

Далее, в useState добавим новый useEffect в котором мы будем записывать значения в localStorage. Что бы не записывать значения в локальное хранилище после каждого рендеринга, мы предоставим второй аргумент и сообщим, что useEffect будет запускаться только в том случае, если изменился наш state.

useCallback и React.memo

Исторически только компоненты класса могли расширять PureComponent или реализовывать свой собственный метод shouldComponentUpdate, чтобы контролировать, когда фактически вызывается его рендер. Однако теперь вы можете использовать React.memo HOC для обеспечения такого же типа управления функциональными компонентами. Это не относится к хукам, но был выпущено в React 16.6 и дополняет API хуков.

Для начала давайте разделим наш компонент на более мелкие компоненты Data и Buttons :

и импортируем их:

Теперь мы можем увидеть, что каждый раз, когда мы нажимаем любую кнопку, рендерится и Data , и Buttons компоненты; так как компонент Data изменился и в следствии этого идет пересборка всего Counter компонента. Buttons компонент у нас не меняться, поэтому давайте сделаем так, что бы он не пересобирался каждый раз, когда мы нажимаем на кнопки. Для это используем React.memo и useCallback .

React.memo - это функция, появившаяся в React 16.6, которая представляет собой компонент более высокого порядка, аналогичный PureComponent , но предназначенный для компонентов функций, а не для классов.

Она сравнивает props компонента и запускает рендер только в том случае, если они изменились. Чтобы использовать его, необходимо обвернуть наш компонент функцией memo .

Но, это еще не всё! Обертки функций increment, decrement, changeName постоянно меняются, поэтому при сравнении нам говорят, что мы внесли изменения.

Мы можем решить это с помощью еще одного React хука. Давайте сначала импортируем хук useCallback и обернем им обработчики increment, decrement, changeName .

useCallback вернет запомненную версию нашего обратного вызова.

Теперь, при нажатии на любую кнопку, рендорится только Data компонент, потому что Buttons компонент остается неизменным и запускается только один раз при начальном рендоринге.

Подпишись на наш канал с тестами по HTML/CSS/JS в Telegram!

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


Photo by Vishal Jadhav on Unsplash

React hooks – это модное в сообществе слово. Скорее всего каждый разработчик React знает, что такое хуки.

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

1. useIdle

С помощью useIdle можно отслеживать, находится ли пользователь на странице в режиме ожидания. Можете передать два параметра: ms – время ожидания и initialState , который позволяет пользователю изначально установить режим ожидания.

2. useInterval

Этот хук можно использовать для функций с интервалами, которые автоматически размонтируют компонент clearInterval . Он также позволяет приостановить интервал, установив задержку равной нулю.

3. useScroll

Используется для прослушивания события прокрутки элемента и перерисовки при прокрутке. Не требует ручного добавления слушателей событий JavaScript.

4. useToggle

5. useTitle

Хук используется для установки заголовка страницы.

6. usePrevious

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

7. useSetState

Этот крючок используется для объединения объектов в их текущем состоянии, аналогично this.setState в компоненте класса. Если вы используете несколько состояний, их можно привести к одному с помощью useSetState .

8. useCookie

Хук используется для возврата текущего значения cookie , обратного вызова обновления и удаления cookie .

9. usePermission

usePermission следует применять для получения permission-статуса API браузера. Передайте имя API, чтобы получить статус.

10. useDebounce

Хук используется для задержки события до завершения времени ожидания. В приведенном ниже коде заданное состояние выполняется после завершения времени ожидания:

11. useGeolocation

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

12. useNetworkState

Приведенный ниже код используется для получения сетевого статуса браузера. useNetworkState можно применять для показа пользователю состояния подключения.

13. useCopyToClipboard

Хотите скопировать текст в буфер обмена? useCopyToClipboard – именно то, что вам необходимо.

14. useFavicon

Крючок useFavicon используется для установки иконки на странице.

15. useError

Применяйте useError для контроля и отправки ошибок.

Заключение

В репозитории react-use на GitHub есть еще несколько настраиваемых хуков. Надеюсь, вы сочтете их полезными. Спасибо за внимание!

Разработчик Брайан Арриета опубликовал в блоге на dev.to пять лучших, на его взгляд, кастомных React Hooks. Вот они.

useTimeout Hook

Позволяет реализовать метод setTimeout JavaScript с помощью декларативного подхода:

usePrevious Hook

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

useInterval Hook

Позволяет реализовать метод setInterval JavaScript с помощью декларативного подхода.

useFetch Hook

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

useConstructor Hook

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

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

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

Мы можем создавать свои хуки, однако React по умолчанию уже предоставляет ряд встроенных хуков:

useState : предназначен для управления состоянием компонентов

useEffect : предназначен для перехвата различного рода изменений в компонентах, которые нельзя обработать внутри компонентов

useContext : позволяет подписываться на контекст React

useReducer : позволяет управлять локальным состоянием сложных компонентов

useCallback : позволяет управлять функциями обратного вызова

useMemo : предназначен для управления мемоизированными (грубо говоря кэшированными) значениями

useRef : возвращать некоторое изменяемое значение, например, ссылку на html-элементы DOM, которыми затем можно управлять в коде JavaScript

useImperativeHandle : настраивает объект, который передается родительскому компоненту при использовании ref

useLayoutEffect : аналогичен хуку useEffect() , но вызывается синхронно после всех изменений в структуре DOM

useDebugValue : предназначен для отображения некоторого значения в целях отладки

Переход от классов к хукам

Рассмотрим простейший пример, как мы можем перейти от классов к хукам. Допустим, у нас есть следующий класс-компонент:

Здесь определен компонент ClickButton, который принимает через props некоторое значение increment . В конструкторе определяется состояние в виде переменной counter , которая равна 0. Кроме того, в классе определяется метод press() , в котором изменяется состояние компонента.

Для изменения состояния вызывается другой метод - incrementCounter , который берет из props значение increment и использует его для увеличения значения переменной counter .

В коде класса-компонента определяется кнопка, по нажатию на которую как раз и вызывается метод press() :

В итоге по нажатию на кнопку мы получим увеличение переменной counter :

Переход от классов-компонентов к хукам в React

Теперь определим аналогичный компонент с использованием хуков:

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

Вначале определяем переменные состояния:

В данном случае определяются две переменных: count и setCount . Переменная count хранит состояние компонента, а переменная setCount позволяет изменять значение переменной count.

В функцию useState() передается число 0 - это то значение, которое по умолчанию получает переменная count .

Для изменения состояния в компоненте определена функция press() , которая выполняет выражение setCount(count + props.increment); - к переменной count прибавляется значение increment из props . Это выражение ( count + props.increment ) и определяет новое значение переменной count . Таким образом, состояние компонента ClickButtonHook будет изменено.

В коде также определена кнопка, по нажатию на которую вызывается метод press() . В итоге мы получим ту же программу, но с использованием хуков. И как видно, этот код несколько короче, чем код класса-компонента.

Подключение хуков

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

Ограничения при использовании хуков

Хуки имеют ряд ограничений при определении и использовании:

Хуки вызываются только на верхнем уровне (top-level) компонента. Они НЕ вызываются внутри циклов, условных конструкций, внутри стандартных функций javascript.

Хуки можно вызывать только из функциональных компонентов React, либо из других хуков. Но их нельзя вызывать из классов-компонентов.

Вы могли подумать, что результат рендера будет следующим, но это не так.

Выше мы говорили о том, что еффект вызывается после того, как компонент примонтирован в DOM. Это значит, что компонент App не может быть примонтирован раньше, чем Children1 и тд. (об этом поговорим ниже). React вызывает компоненты сверху вниз, выполняя еффекты от ребенка к родителю, по завершению работы для текущей ноды (завершением работы для ноды считается тот момент, когда у него нет не обработанных дочерних елементов), а если у текущего компонента есть ребенок - он вызывает его и так до конца. (Все еффекты сохраняются в Effect List ) План работы компонента выше:

  • Выполнить render App (есть дочерний компонент), значит не вызываем еффект, идем к дочернему елементу.
  • Выполнить render С1 (есть дочерний компонент), значит не вызываем еффект, идем к дочернему елементу.
  • Выполнить render С3 (дочернего елемента нет), значит вызываем еффект.
    • Вернуться в C1 и проверить еще дочерние елементы, если нет - вызвать еффект.
    • вернуться к родителю App и выполнить его еффект.

    Результат выполнения работы С3 => C1 => C2 => App .

    Не знание этого может привести к багам.

    Этот еффект обладает таким же API, как и useEffect , стем отличием, что он вызывается синхронно, после всех вычислений мутаций в DOM, тоесть блокирует отрисовку браузера, в то время как useEffect вызывается асинхронно и не блокирует рендер (это и есть основное различие - когда они срабатывают). Изменения, запланированные через useLayoutEffect, будут синхронно выполнены перед тем, как браузер получит возможность выполнить отрисовку. Колбек, которые возвращается из еффекта, вызывается так же как и в первом случае, но перед ним. Асинхронное поведение нужно для быстрого отклика приложения, потому что еффекту не нужно быть синхронным (есть редкие случаи, например чтение елементов DOM или синхронного повторного рендеринга).

    Синхронный вызов значит, что useLayoutEffect будет выполнен раньше useEffect , это пригодится при работе с DOM (анимации, вычисление позиции, высоты). В примере ниже, при клике будет обновление дома, сначала 0, потом новое значение, но у нас не будет мигания, потому что еффект вызван синхронно, до того, как браузер успеет сделать рендер. Если заменить вызов на useEffect , то будет мигание пример.

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

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

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

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

    Хук принимает колбек и возвращает мемоизированное значение. useMemo довольно "дорогой" в исполнении и его так же не стоит использовать постоянно, он работает в два этапа:

    • Маунт (mountMemo)
    • Обновление (updateMemo)

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

    useRef возвращает изменяемый ref-объект, свойство .current которого инициализируется переданным аргументом (initialValue). Возвращаемый объект будет сохраняться в течение всего времени жизни компонента. Хук создает обьект с изменяемым свойством current , в которое передается начальное значение, при каждом рендере ссылка на обьект будет одна и таже.

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

    Каждый вызов usePrevious - мутирует свойство current, в примере получаем старое значение, потому что еффект выполняется после того, как предыдущее значение было получено из хука.

    Для каждого узла у реакта есть собственный тип, в примере можно использовать все методы инпута (click, focus, blur) - это может пригодиться для императивного доступа к элементам.

    Принимает объект контекста (значение, возвращённое из React.createContext) и возвращает текущее значение контекста для этого контекста. Текущее значение контекста определяется пропом value ближайшего Context.Provider над вызывающим компонентом в дереве. При обновлении ближайшего контекста - вызывается повторное обновление компонента, использующего этот хук. Хук не изменяет значение контекста, он толькопозволяет подписать компонент на контекст. Provider все еще нужен, для инициализации этого контекста.

    Стандартный пример использования контекста с темой. Provider получает значение по умолчанию и передает его в контекст, его можно использовать в любом компоненте ниже по дереву (так же провайдер можно увидtть в инструментах разработчика). Что бы получить контекст на одном уровне с провайдером, нужно использовать Consumer . В value провайдера можно передавать любые значения или обработчики, которые потом можно использовать в дочерних компонентах. Контекст не стоит использовать в больших приложениях, для этого есть различные стейт менеджеры.

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

    Нужен для использования в пользовательских хуках - отображает метку в dev tools , напротив хука, для более удобной отладки.

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

    Что бы в каждом проекте не писать типовые хуки, есть библиотка react-use, в которой достаточно много разнообразных хуков и их описание.

    У хуков есть свои правила, которые показаны выше, почему же эти правила нужны?

    Чтобы назначить поведение функциональному компоненту, нужно иметь возможность как-то ассоциировать это поведение с этим компонентом.

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

    Как выше говорили, React хранит состояние вне функции, поэтому после каждого рендера оно не теряется, кроме того, этот код не доступен другим компонентам, но он есть в пространстве текущего компонента. Для примера назовем setState - сеттер, а состояние state. пример Реализацию можно представить ввиде двух массивов - state, setters. Каждый вызов хука React по его id - сохраняет в массив, к конкретной fiber ноде , при каждом обновлении этот индекс сбрасывается, при каждом вызове хука - увеличивается. Поэтому если вызвать хук по условию - это приведет к ошибке, ведь React не знает, что не нужно увеличивать счетчик и вернет не верный ответ.

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