Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
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
- Host: GitHub
- URL: https://github.com/streamich/use-media
- Owner: streamich
- License: unlicense
- Created: 2018-11-03T13:54:28.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2024-09-17T12:11:12.000Z (3 months ago)
- Last Synced: 2024-09-17T22:01:12.693Z (3 months ago)
- Language: TypeScript
- Size: 1010 KB
- Stars: 522
- Watchers: 4
- Forks: 24
- Open Issues: 24
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- fucking-awesome-react-hooks - `use-media`
- awesome-react-hooks-cn - `use-media`
- awesome-react-hooks - `use-media`
- awesome-react-hooks - `use-media`
- awesome-react-hooks - use-media - useMedia React hook to track CSS media query state (Packages)
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()}
);
}
```