Главная » Статьи » Недооцененные хуки React

Недооцененные хуки React

Недооцененные хуки React

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

Если вы разработчик React и предпочитаете писать функциональные компоненты вместо классовых, то эта статья для вас; мы рассмотрим три полезных, но несколько недооцененных хука React, с которыми вы, возможно, не так хорошо знакомы: useImperativeHandle, useLayoutEffect и useDebugValue. Мы рассмотрим варианты их использования, синтаксис и несколько примеров кода. Давайте начнем!

React хук useImperativeHandle

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

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

Синтаксис:

useImperativeHandle(ref, createHandle, [dependencies])

ref: ссылка, переданная от родительского компонента

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

dependencies: массив значений, который заставляет хук перезапускаться при изменении

Варианты применения

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

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

Определение состояния в родительском компоненте приведет к тому, что родительский компонент и его дочерние элементы будут повторно отображаться каждый раз при нажатии модальной кнопки, поэтому я хотел, чтобы состояние было в дочернем компоненте. Вместо этого я сохранил модальное состояние в модальном компоненте, используя useImperativeHandle и forwardRef. Рассмотрим код ниже:

import React, { useRef } from 'react';
import Child from './Child'; const ParentComponent = () => { const childRef = useRef(null); const handleOpenModal = (value) => { childRef.current.openModal(value); } return ( <div> <p>This is a parent component</p> <Child ref={childRef}/> <button onClick={() => handleOpenModal(true)}>Open modal</button> </div> ) } export default ParentComponent;

Выше мы определили ссылку, которую передали дочернему компоненту. В приведенном ниже коде ref будет первым параметром, переданным в useImperativeHandle.

Мы также определили функцию handleOpenModal, которая возвращает функцию openModal, переданную из дочернего компонента с помощью childRef.current.openModal(value). Эта функция вызывается при нажатии кнопки. Дочерний компонент должен выглядеть так, как показано ниже:

import React, { forwardRef, useImperativeHandle, useState } from 'react'; function ChildComponent (props, ref) { const [openModal, setOpenModal] = useState(false); useImperativeHandle(ref, () => ({ openModal: (value) => setOpenModal(value), })); if(!openModal) return null; return ( <div> <p>This is a modal!</p> <button onClick={() => setOpenModal(false)}> Close </button> </div> ) } export default forwardRef(ChildComponent);

Мы обернули дочерний компонент в forwardRef, чтобы вызвать функцию openModal, определенную в useImperativeHandle. В родительском компоненте состояние, определенное в дочернем компоненте, изменяется, вызывая повторную визуализацию только дочернего компонента. Проблема решена!

React хук useLayoutEffect

Как и хук useEffect, хук useLayoutEffect позволяет выполнять дополнительные эффекты, такие как вызовы API, настройку подписок и манипулирование DOM вручную в функциональном компоненте.

Хотя React запускает как useEffect, так и useLayoutEffect после выполнения обновлений DOM, useLayoutEffect вызывается до того, как браузер отрисует эти обновления для просмотра пользователями, синхронно, а useEffect вызывается после того, как браузер отрисует эти обновления, асинхронно.

Таким образом, браузер не может отображать какие-либо обновления, пока не запустится useLayoutEffect. По этой причине вы в основном будете использовать useEffect, который выполняет роль показывает пользователям что-то вроде загрузчика в браузере, пока выполняются дополнительные эффекты.

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

Синтаксис:

useLayoutEffect(callback, [dependencies])

callback: функция, содержащая логику побочного эффекта

dependencies: массив зависимостей.

Функция обратного вызова запускается снова при изменении значения любой из зависимостей. Рассмотрим код ниже:

import React, { useState, useLayoutEffect, useEffect } from 'react'; const TestHooks = () => { const [randomNumber, setRandomNumber] = useState(0); useEffect(() => { if (randomNumber === 0) { setRandomNumber(Math.random() * 1000); } }, [randomNumber]); return ( <div className='App'> <p>{randomNumber}</p> <button onClick={() => setRandomNumber(0)}> Change value </button> </div> ); }; export default TestHooks;

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

Теперь замените useEffect на useLayoutEffect и снова нажмите кнопку. Переход к следующему случайному числу более плавный.

Код с использованием useEffect:

DOM обновления => Браузер отображает обновление для просмотра пользователем => useEffect => другое обновление DOM => Браузер отображает второе обновление для просмотра пользователем

Код с использованием useLayoutEffect:

DOM обновления => useLayoutEffect => другое обновление DOM => Браузер отображает общее обновление для просмотра пользователем

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

Варианты применения

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

Мы можем решить эту проблему с помощью useLayoutEffect следующим образом:

import { useLayoutEffect } from "react";
import { useLocation } from "react-router-dom"; export default function ScrollToTop() { const { pathname } = useLocation(); useLayoutEffect(() => { window.scrollTo(0, 0); }, [pathname]); return null;
}

Файл index.js выглядит следующим образом:

function Index() { return ( <Router> <ScrollToTop /> <App /> </Router> );
}

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

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

React хук useDebugValue

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

Синтаксис:

useDebugValue(value)

Если вы знакомы с React DevTools, то знаете, что всякий раз, когда встроенный хук React, такой как useState или useRef, используется в пользовательском хуке, он будет заниматся отладкой соответствующих значений в React DevTools.

Например, рассмотрим пользовательский хук ниже:

import { useDebugValue, useState } from "react" export default function useCustomHook() { const [name, setName] = useState("") const [address, setAddress] = useState("") return [name, setName, address, setAddress]
}

Давайте вызовем пользовательский хук в App.js и проверим DevTools:

import useCustomHook from './useCustomHook'; function App() { useCustomHook(); return ( <> <div className="App"> <p>hey</p> </div> </> );
} export default App;

Проверяя React DevTools, мы видим, что значение для useState уже зарегистрировано для нас. У нас есть два состояния для name и address:

Недооцененные хуки React

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

Однако если бы мы создавали сложный пользовательский хук для использования в различных компонентах, нам понадобился бы способ отслеживать, какие значения принадлежат каким состояниям во время отладки. Для этого мы можем использовать useDebugValue для отображения метки для значений в пользовательских хуках в React DevTools. См. обновленный пользовательский хук ниже:

import { useDebugValue, useState } from "react" export default function useCustomHook() { const [name, setName] = useState("") const [address, setAddress] = useState("") useDebugValue(name ? 'Name has been set' : 'Name has not been set') return [name, setName, address, setAddress]
}

Обновленный DevTools выглядит следующим образом:

Недооцененные хуки React

Мы можем отслеживать значение name по мере изменения его состояния, используя useDebugValue, поэтому нам не нужно угадывать, каково его значение. useDebugValue полезен, когда состояние хука не сразу видно в DevTools.

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

useDebugValue(date, date => date.toDateString());

Заключение

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

UseImperativeHandleHook позволяет нам предоставлять значение, состояние или функцию внутри дочернего компонента родительскому компоненту. TheuseLayoutEffect позволяет нам выполнять дополнительные эффекты, такие как вызовы API, настройка подписок и ручное управление DOM в функциональном компоненте. Наконец, хук useDebugValue упрощает разработчикам регистрацию информации в React DevTools.

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

Автор: Chiamaka Umeh

Источник: blog.logrocket.com

Редакция: Команда webformyself.

Читайте нас в Telegram, VK, Яндекс.Дзен