Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/streamich/use-media

useMedia React hook to track CSS media query state
https://github.com/streamich/use-media

Last synced: about 1 month ago
JSON representation

useMedia React hook to track CSS media query state

Awesome Lists containing this project

README

        

# use-media

`useMedia` React sensor hook that tracks state of a CSS media query.

## Install

You can install `use-media` with npm

```bash
npm install --save use-media
```

or with yarn
```bash
yarn add use-media
```

## Usage

With `useEffect`

```jsx
import useMedia from 'use-media';
// Alternatively, you can import as:
// import {useMedia} from 'use-media';

const Demo = () => {
// Accepts an object of features to test
const isWide = useMedia({minWidth: '1000px'});
// Or a regular media query string
const reduceMotion = useMedia('(prefers-reduced-motion: reduce)');

return (


Screen is wide: {isWide ? '😃' : '😢'}

);
};
```

With `useLayoutEffect`

```jsx
import {useMediaLayout} from 'use-media';

const Demo = () => {
// Accepts an object of features to test
const isWide = useMediaLayout({minWidth: '1000px'});
// Or a regular media query string
const reduceMotion = useMediaLayout('(prefers-reduced-motion: reduce)');

return (


Screen is wide: {isWide ? '😃' : '😢'}

);
};
```

## Testing

Depending on your testing setup, you may need to mock `window.matchMedia` on components that utilize the `useMedia` hook. Below is an example of doing this in `jest`:

**`/test-utilities/index.ts`**

```jsx
import {mockMediaQueryList} from 'use-media/lib/useMedia';
// Types are also exported for convienence:
// import {Effect, MediaQueryObject} from 'use-media/lib/types';

export interface MockMatchMedia {
media: string;
matches?: boolean;
}

function getMockImplementation({media, matches = false}: MockMatchMedia) {
const mql: MediaQueryList = {
...mockMediaQueryList,
media,
matches,
};

return () => mql;
}

export function jestMockMatchMedia({media, matches = false}: MockMatchMedia) {
const mockedImplementation = getMockImplementation({media, matches});
window.matchMedia = jest.fn().mockImplementation(mockedImplementation);
}
```

**`/components/MyComponent/MyComponent.test.tsx`**

```jsx
const mediaQueries = {
mobile: '(max-width: 767px)',
prefersReducedMotion: '(prefers-reduced-motion: reduce)',
};

describe('', () => {
const defaultProps: Props = {
duration: 100,
};

afterEach(() => {
jestMockMatchMedia({
media: mediaQueries.prefersReducedMotion,
matches: false,
});
});

it('sets `duration` to `0` when user-agent `prefers-reduced-motion`', () => {
jestMockMatchMedia({
media: mediaQueries.prefersReducedMotion,
matches: true,
});

const wrapper = mount();
const child = wrapper.find(TransitionComponent);

expect(child.prop('duration')).toBe(0);
});
});
```

## Storing in Context

Depending on your app, you may be using the `useMedia` hook to register many `matchMedia` listeners across multiple components. It may help to elevate these listeners to `Context`.

**`/components/MediaQueryProvider/MediaQueryProvider.tsx`**

```jsx
import React, {createContext, useContext, useMemo} from 'react';
import useMedia from 'use-media';

interface Props {
children: React.ReactNode;
}

export const MediaQueryContext = createContext(null);

const mediaQueries = {
mobile: '(max-width: 767px)',
prefersReducedMotion: '(prefers-reduced-motion: reduce)',
};

export default function MediaQueryProvider({children}: Props) {
const mobileView = useMedia(mediaQueries.mobile);
const prefersReducedMotion = useMedia(mediaQueries.prefersReducedMotion);
const value = useMemo(() => ({mobileView, prefersReducedMotion}), [
mobileView,
prefersReducedMotion,
]);

return (

{children}

);
}

export function useMediaQueryContext() {
return useContext(MediaQueryContext);
}
```

**`/components/App/App.tsx`**

```jsx
import React from 'react';
import MediaQueryProvider from '../MediaQueryProvider';
import MyComponent from '../MyComponent';

export default function App() {
return (





);
}
```

**`/components/MyComponent/MyComponent.tsx`**

```jsx
import React from 'react';
import {useMediaQueryContext} from '../MediaQueryProvider';

export default function MyComponent() {
const {mobileView, prefersReducedMotion} = useMediaQueryContext();

return (


mobileView: {Boolean(mobileView).toString()}


prefersReducedMotion: {Boolean(prefersReducedMotion).toString()}



);
}
```