Как написать кастомный React-хук для отслеживания ресайза окна или блока
Работая с динамическими интерфейсами, часто возникает необходимость реагировать на изменение размеров окна или конкретного DOM-элемента. Это может быть полезно при построении адаптивных компонентов, виртуальных списков, графиков и даже drag’n’drop-интерфейсов.
Вместо того чтобы дублировать логику в каждом компоненте, правильнее создать переиспользуемый React-хук.
📦 Хук для отслеживания размеров окна: useWindowSize
Начнём с базового случая — отслеживания размеров браузерного окна.
import { useState, useEffect } from 'react';
export function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
// Вызовем при монтировании
handleResize();
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
Применение:
const MyComponent = () => {
const { width, height } = useWindowSize();
return <div>Размер окна: {width} x {height}</div>;
};
✅ Плюсы:
- Простота
- Подходит для layout-компонентов
- Работает во всех браузерах
⚠️ Минусы:
- Работает только с
window
, не с элементами - Может вызывать лишние ререндеры при частом ресайзе
📏 Хук для отслеживания размеров DOM-элемента: useResizeObserver
Если нужно отслеживать размеры конкретного блока, например <div ref={...}>
, лучше использовать ResizeObserver
.
import { useEffect, useState, useRef } from 'react';
export function useResizeObserver() {
const ref = useRef(null);
const [size, setSize] = useState({ width: 0, height: 0 });
useEffect(() => {
if (!ref.current) return;
const observer = new ResizeObserver(([entry]) => {
if (entry.contentRect) {
setSize({
width: entry.contentRect.width,
height: entry.contentRect.height
});
}
});
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, []);
return [ref, size];
}
Применение:
const ResizableBox = () => {
const [ref, size] = useResizeObserver();
return (
<div ref={ref} style={{ resize: 'both', overflow: 'auto', border: '1px solid #ccc' }}>
<p>Ширина: {size.width}px</p>
<p>Высота: {size.height}px</p>
</div>
);
};
✅ Плюсы:
- Поддерживает наблюдение за любым DOM-элементом
- Работает на низком уровне через
ResizeObserver
- Не зависит от окна
⚠️ Минусы:
- Не работает в очень старых браузерах (IE)
- Нужно обрабатывать
ref
аккуратно (например, при условной отрисовке)
🚀 Вывод
Кастомные хуки — это мощный способ инкапсуляции логики в React. Хуки useWindowSize
и useResizeObserver
решают разные задачи, и оба удобны в повседневной разработке. В своей практике я чаще использую ResizeObserver
, особенно в компонентах, чувствительных к layout’у: графики, таблицы, текстовые блоки с обрезкой.
При этом, оборачивая такую логику в кастомные хуки, вы делаете интерфейс переиспользуемым, чистым и предсказуемым — именно за это мы и любим React.