Практика: набор полезных хуков
Собрали набор хуков, которые пригодятся в реальных проектах.
usePrevious
Хранит предыдущее значение:
function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
useInterval
Управляемый интервал:
function useInterval(callback: () => void, delay: number | null) {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (delay === null) return;
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}, [delay]);
}
useWindowSize
Размер окна:
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
function handle() {
setSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener('resize', handle);
return () => window.removeEventListener('resize', handle);
}, []);
return size;
}
useOnlineStatus
Онлайн/оффлайн:
function useOnlineStatus() {
const [online, setOnline] = useState(navigator.onLine);
useEffect(() => {
const on = () => setOnline(true);
const off = () => setOnline(false);
window.addEventListener('online', on);
window.addEventListener('offline', off);
return () => {
window.removeEventListener('online', on);
window.removeEventListener('offline', off);
};
}, []);
return online;
}
useForm
Мини-форма:
function useForm<T extends Record<string, unknown>>(initial: T) {
const [values, setValues] = useState(initial);
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const { name, value, type, checked } = e.target;
setValues((v) => ({
...v,
[name]: type === 'checkbox' ? checked : value,
}));
}
function reset() {
setValues(initial);
}
return { values, handleChange, reset, setValues };
}
useKeyPress
Отслеживает нажатие клавиши:
function useKeyPress(targetKey: string): boolean {
const [pressed, setPressed] = useState(false);
useEffect(() => {
function down(e: KeyboardEvent) {
if (e.key === targetKey) setPressed(true);
}
function up(e: KeyboardEvent) {
if (e.key === targetKey) setPressed(false);
}
window.addEventListener('keydown', down);
window.addEventListener('keyup', up);
return () => {
window.removeEventListener('keydown', down);
window.removeEventListener('keyup', up);
};
}, [targetKey]);
return pressed;
}
useCopyToClipboard
Копирование в буфер:
function useCopyToClipboard() {
const [copied, setCopied] = useState(false);
async function copy(text: string) {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
}
return { copied, copy };
}
useIntersectionObserver
Отслеживает появление в viewport:
function useIntersectionObserver<T extends HTMLElement>() {
const ref = useRef<T>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
if (!ref.current) return;
const observer = new IntersectionObserver(([entry]) => {
setVisible(entry.isIntersecting);
});
observer.observe(ref.current);
return () => observer.disconnect();
}, []);
return { ref, visible };
}
Как проектировать свои хуки
- Начни с логики в компоненте
- Если логика повторяется — вынеси в хук
- Название начинается с
use - Хук возвращает то, что нужно компоненту
- Не бойся возвращать объект или массив
- Держи хук сфокусированным на одной задаче
Итоги
- Кастомные хуки — стандарт современного React
- Много готовых хуков можно найти в библиотеках (react-use, usehooks-ts)
- Свои хуки лучше держать маленькими
- Название с
useобязательно - Хук должен быть переиспользуемым