Skip to content

Интеграция с React ​

Импортируйте из responsive-media/react.

ts
import {
  useResponsive,
  useBreakpoints,
  useMediaQuery,
  useContainerState,
} from 'responsive-media/react'

Типизированные хуки из вашего конфига ​

defineResponsive(config, options?)

Применяет config к общему состоянию и возвращает useResponsive, useBreakpoints и useResponsiveValue, причём состояние и ключи брейкпоинтов выводятся из config — без обобщённого параметра, который нужно писать вручную. Вызовите её в одном модуле и импортируйте хуки оттуда.

ts
// 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>.

tsx
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-состояния.

tsx
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 на сервере).

tsx
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.

tsx
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, поэтому при пересекающихся брейкпоинтах ставьте самый узкий первым.

tsx
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.

tsx
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 и гидратация.