Интеграция с React
Импортируйте из responsive-media/react.
import {
useResponsive,
useBreakpoints,
useMediaQuery,
useContainerState,
} from 'responsive-media/react'Типизированные хуки из вашего конфига
defineResponsive(config, options?)
Применяет config к общему состоянию и возвращает useResponsive, useBreakpoints и useResponsiveValue, причём состояние и ключи брейкпоинтов выводятся из config — без обобщённого параметра, который нужно писать вручную. Вызовите её в одном модуле и импортируйте хуки оттуда.
// responsive.ts
import { defineResponsive } from 'responsive-media/react'
export const { useResponsive, useBreakpoints } = defineResponsive(
{
mobile: [{ type: 'max-width', value: 600 }],
smallTablet: [{ type: 'max-width', value: 850 }],
desktop: [{ type: 'min-width', value: 961 }],
},
{ order: ['mobile', 'smallTablet', 'desktop'] },
)useResponsive()возвращает{ mobile: boolean; smallTablet: boolean; desktop: boolean }.useBreakpoints()принимает только эти ключи:isAbove('nope')— ошибка компиляции, аcurrentимеет тип'mobile' | 'smallTablet' | 'desktop' | null.
Вызов заменяет конфигурацию, заданную ранее через setResponsiveConfig(), потому что у них одно общее состояние.
Responsive-состояние
useResponsive<T>()
Возвращает текущее responsive-состояние. Ререндерится только при изменении состояния. Внутри использует useSyncExternalStore. Принимает обобщённый параметр типа для сужения возвращаемого состояния — см. ConfigToState<T>.
import { useResponsive } from 'responsive-media/react'
type MyState = { sm: boolean; lg: boolean }
function App() {
const { sm, lg } = useResponsive<MyState>()
return sm ? <MobileNav /> : <DesktopNav />
}Хелперы брейкпоинтов
useBreakpoints()
Возвращает упорядоченные хелперы брейкпоинтов. Ререндерится при изменении responsive-состояния.
import { useBreakpoints } from 'responsive-media/react'
function Nav() {
const { current, isAbove, isBelow, between } = useBreakpoints()
return (
<>
<span>Current: {current}</span>
{isAbove('sm') ? <DesktopNav /> : <MobileNav />}
{between('sm', 'lg') && <TabletBanner />}
</>
)
}В отличие от Vue,
current— обычнаяstring | null(не ref). Ререндеры запускаются черезuseSyncExternalStore.
Media Query
useMediaQuery(query)
boolean
Отслеживает сырую строку CSS media query. SSR-безопасно (возвращает false на сервере).
import { useMediaQuery } from 'responsive-media/react'
function ThemeToggle() {
const isDark = useMediaQuery('(prefers-color-scheme: dark)')
const canHover = useMediaQuery('(hover: hover)')
return <button className={isDark ? 'dark' : 'light'}>Toggle</button>
}Состояние контейнера
useContainerState(ref, config, options?)
Отслеживает размеры элемента и возвращает объект состояния, ключи которого выводятся из config. Настраивает и убирает ResizeObserver через useEffect.
import { useRef } from 'react'
import { useContainerState } from 'responsive-media/react'
function Card() {
const ref = useRef<HTMLDivElement>(null)
const { compact, wide } = useContainerState(ref, {
compact: [{ type: 'max-width', value: 300 }],
wide: [{ type: 'min-width', value: 600 }],
})
return (
<div ref={ref}>{compact ? <CompactLayout /> : wide ? <WideLayout /> : <DefaultLayout />}</div>
)
}
configиoptionsсчитаются статичными после монтирования. Оборачивайте вuseMemo, если они меняются.
Если целевой элемент ещё не подключён на момент первого запуска этого хука (например, он находится за условно монтируемым позже блоком или порталом), хук не сдаётся — ограниченный цикл повторных попыток на
requestAnimationFrameпродолжает проверять ref, пока элемент не появится, и только тогда настраиваетResizeObserver.
Значения по брейкпоинтам
useResponsiveValue(map, fallback?)
T | undefined
Возвращает значение первого активного брейкпоинта из map или fallback, если ни один не активен. Решает порядок в map: побеждает первый ключ, равный true, поэтому при пересекающихся брейкпоинтах ставьте самый узкий первым.
import { useResponsiveValue } from 'responsive-media/react'
function Gallery() {
const columns = useResponsiveValue({ mobile: 1, tablet: 2, desktop: 4 })
return <Grid columns={columns} />
}У хуков из defineResponsive() ключи map проверяются по вашему конфигу.
Пользовательские предпочтения
useUserPreferences()
{ dark, light, reducedMotion, highContrast, lowContrast, noHover, coarsePointer, forcedColors, print }
Предпочтения пользователя по внешнему виду и доступности — AccessibilityPreset как готовое состояние, настраивать ничего не нужно. Он читает собственное состояние, поэтому никогда не мешает вашим брейкпоинтам. На сервере каждый ключ равен false.
import { useUserPreferences } from 'responsive-media/react'
function Hero() {
const { reducedMotion } = useUserPreferences()
return reducedMotion ? <StillImage /> : <Parallax />
}Размер вьюпорта
useViewportSize(options?)
{ width: number; height: number }
Размер окна в пикселях — для случаев, когда нужно число, а не брейкпоинт. На сервере он 0 на 0, а объект сохраняет идентичность между рендерами, пока размер не изменится.
throttle—number· по умолчанию:100. Миллисекунды между обновлениями при изменении размера окна;0обновляет на каждое событие.
Гидратация
Пока React гидратирует, хуки возвращают серверный снимок (getSsrState()), поэтому первый рендер на клиенте совпадает с серверным HTML, а потом React перерисовывает страницу с настоящим значением без recoverable-ошибки. Опцией ssrState выбирается то, что рендерит сервер — см. SSR и гидратация.