5 кастомных React хуков, которые улучшат ваш код
2 года назад·3 мин. на чтение
В этой статье рассмотрим несколько очень полезных React хуков.
1. Хук useWindowSize
- хук для получения размера экрана
Я уверен, что в некоторых проектах, над которыми вы работали, вам нужно было получить ширину и высоту окна пользователя.
Так вот, теперь у вас есть хук для этого, так что вы можете сделать это еще проще, чем раньше.
Этот хук может быть особенно полезен при реализации отзывчивого дизайна, когда по какой-то причине вам нужно запустить определенный код при определенных размерах. Более продвинутый вариант этого хука можно найти в статье “Хук useResize для отслеживания ширины экрана в ReactJS”.import { useState, useEffect } from "react"; interface WindowSize { width: number; height: number; } const useWindowSize = (): WindowSize => { const [windowSize, setWindowSize] = useState<WindowSize>({ width: window.innerWidth, height: window.innerHeight, }); useEffect(() => { const handleResize = () => { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); }; window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; }, []); return windowSize; };
2️. Хук useKeyPress
- хук для определения нажатия клавиши
Следующий хук позволяет определить, когда нажата определенная клавиша. Это может вызвать события или действия, основанные на нажатой клавише. Например, для закрытия модального окна, отправки формы и т.д.
Пример использования:import { useState, useEffect } from "react"; const useKeyPress = (targetKey: string): boolean => { const [keyPressed, setKeyPressed] = useState(false); const downHandler = ({ key }: KeyboardEvent) => { if (key === targetKey) { setKeyPressed(true); } }; const upHandler = ({ key }: KeyboardEvent) => { if (key === targetKey) { setKeyPressed(false); } }; useEffect(() => { window.addEventListener("keydown", downHandler); window.addEventListener("keyup", upHandler); return () => { window.removeEventListener("keydown", downHandler); window.removeEventListener("keyup", upHandler); }; }, []); return keyPressed; };
const closeModalKeyPress = useKeyPress("Escape");
3. Хук useInterval
- хук для вызова функции setInterval
Этот хук позволяет вам использовать функцию setInterval
в качестве хука. Как и функция setInterval
, этот хук имеет множество применений, например, анимация, обновление данных через регулярные промежутки времени или даже установка таймера.
Можно использовать этот хук следующим образом:import { useState, useEffect, useRef } from "react"; const useInterval = (callback: () => void, delay: number | null) => { const savedCallback = useRef<() => void>(); useEffect(() => { savedCallback.current = callback; }, [callback]); useEffect(() => { function tick() { savedCallback.current && savedCallback.current(); } if (delay !== null && delay > 0) { let id = setInterval(tick, delay); return () => clearInterval(id); } else { tick(); } }, [delay]); };
const [count, setCount] = useState(0); useInterval(() => { setCount(count + 1); }, 1000);
4. Хук useDebounce
Теперь мы поговорим о хуке, который выполняет функцию только после того, как пройдет определенное количество времени без ее вызова.
Это полезно, например, для ограничения скорости вызовов API или обновления состояния при изменении ввода, например, когда вы набираете текст в поисковой строке.
Пример использования:import { useEffect, useRef } from 'react'; export function useDebounce(callback:Function, timeout: number = 200, deps: Array<any> = []) { const data = useRef({ firstTime: true }); useEffect(() => { const { firstTime, clearFunc } = data.current; const handler = setTimeout(() => { if (clearFunc && typeof clearFunc === 'function') { clearFunc(); } data.current.clearFunc = callback(); }, timeout); return () => { clearTimeout(handler); }; }, [timeout, ...deps]); } export default useDebounce;
const [inputValue, setInputValue] = useState(""); useDebounce(() => { // вызов APIl }, 500);
5️. Хук useThrottle
Throttle означает, что функция будет выполняться один раз за каждый заданный промежуток времени. Это может быть полезно для предотвращения быстрого запуска слишком большого количества вызовов API или событий.
Пример:import { useEffect, useRef } from 'react'; export const useThrottle = (callback:Function, delay: number = 200, deps: Array<any> = []) => { const lastRan = useRef(Date.now()); useEffect( () => { const handler = setTimeout(function() { if (Date.now() - lastRan.current >= delay) { callback(); lastRan.current = Date.now(); } }, delay - (Date.now() - lastRan.current)); return () => { clearTimeout(handler); }; }, [delay, ...deps], ); }; export default useThrottle;
Еще больше крутых хуков вы можете найти в каталоге хуков.const [inputValue, setInputValue] = useState(""); useThrottle(() => { // вызов API }, 500);
Хуки useTransition и useDeferredValue в ReactJS 18
2 года назад·3 мин. на чтение
В React 18, релиз которого произошел в марте 2022, появилось много новых инструментов для написания производительных и отзывчивых приложений. Одним из заметных изменений является механизм рендеринга с новой ключевой концепцией: конкурентный рендеринг (concurrent rendering).
В этой статье повнимательнее рассмотрим два новых хука:
Какое обновление можно считать срочным, а какое обычным?
Хук
До React 18 все обновления состояния помечались как "срочные". Это означает, что все обновления состояния обрабатывались одинаково с одинаковым приоритетом.
С помощью
Когда использовать
Одним из примеров может быть список товаров с параметрами фильтрации.
Когда вы переключаете чекбоксы, чтобы выбрать размер или цвет одежды, вы ожидаете, что чекбоксы сразу же отобразят отмеченное или снятое состояние.
А сам список товаров, которые необходимо обновить согласно фильтрам, может быть отдельным и менее срочным обновлением.
Как использовать
Хук
Когда использовать
С помощью
Как использовать
useTransition()
и useDeferredValue()
.
Эти два хука дают возможность определять приоритет обновления состояния, или, скорее, указывать, является ли обновление менее важным, чем другие, и откладывать его в пользу более срочных.
- Срочные обновления: отражают прямое взаимодействие, такое как набор текста, клики, нажатия и т. д., т.е. то с чем взаимодействует пользователь. Когда вы вводите текст в поле ввода, вы хотите сразу увидеть введенный вами текст. В противном случае UI будет казаться медленным и подлагивать. Поэтому мы хотим сделать такие обновления приоритетным.
- Обычные обновления: переход пользовательского интерфейса из одного вида в другой. Пользователи знают, что представление должно измениться или обновиться (например, когда ожидается ответ на запрос данных). Даже если есть небольшая задержка, это можно рассматривать как ожидаемое поведение, и это не будет восприниматься как медлительность приложения.
Хук useTransition()
и функция startTransition()
До React 18 все обновления состояния помечались как "срочные". Это означает, что все обновления состояния обрабатывались одинаково с одинаковым приоритетом.
С помощью useTransition()
теперь можно пометить некоторые обновления состояния как несрочные.
Когда использовать useTransition()
?
Одним из примеров может быть список товаров с параметрами фильтрации.
Когда вы переключаете чекбоксы, чтобы выбрать размер или цвет одежды, вы ожидаете, что чекбоксы сразу же отобразят отмеченное или снятое состояние.
А сам список товаров, которые необходимо обновить согласно фильтрам, может быть отдельным и менее срочным обновлением.
Как использовать useTransition()
?
function App() { const [isPending, startTransition] = useTransition(); const [searchQuery, setSearchQuery] = useState(''); // запрос данных, который занимает некоторое время const filteredResults = getProducts(searchQuery); function handleQueryChange(event) { startTransition(() => { // оборачивая setSearchQuery() в startTransition(), // мы помечаем эти обновления как менее важные setSearchQuery(event.target.value); }); } return ( <div> <input type="text" onChange={handleQueryChange} /> {isPending && <span>Loading...</span>} <ProductsList results={filteredResults} /> </div> ); }
Хук useDeferredValue()
useDeferredValue()
очень похож на useTransition()
в том, что он позволяет отложить несрочное обновление состояния, но применяется его к части дерева.
Это похоже методы debounce и throttle, которые мы часто используем для отложенных обновлений. React будет работать с такими обновлениями, как только завершатся срочные обновления.
Когда использовать useDeferredValue()
?
С помощью useTransition()
вы сами решаете, когда конкретное обновление состояния может быть помечено как менее срочное. Но иногда такой возможности может и не быть, например, если фрагмент кода находится в сторонней библиотеке.
В таких случаях можно воспользоваться хуком useDeferredValue()
. С помощью useDeferredValue()
вы можете обернуть значение и пометить его изменения как менее важные и, следовательно, отложить повторный рендеринг.
useDeferredValue()
будет возвращать предыдущее значение до тех пор, пока есть более срочные обновления для завершения и отображения дерева с обновленным значением.
Как использовать useDeferredValue()
?
function ProductsList({ results }) { // deferredResults получат обновленные данные // когда завершатся срочные обновления const deferredResults = useDeferredValue(results); return ( <ul> {deferredResults.map((product) => ( <li key={product.id}>{product.title}</li> ))} </ul> ); }