UNPKG

1.96 kBJavaScriptView Raw
1import useEffect from './useIsomorphicEffect';
2import { useState } from 'react';
3const matchersByWindow = new WeakMap();
4const getMatcher = (query, targetWindow) => {
5 if (!query || !targetWindow) return undefined;
6 const matchers = matchersByWindow.get(targetWindow) || new Map();
7 matchersByWindow.set(targetWindow, matchers);
8 let mql = matchers.get(query);
9 if (!mql) {
10 mql = targetWindow.matchMedia(query);
11 mql.refCount = 0;
12 matchers.set(mql.media, mql);
13 }
14 return mql;
15};
16/**
17 * Match a media query and get updates as the match changes. The media string is
18 * passed directly to `window.matchMedia` and run as a Layout Effect, so initial
19 * matches are returned before the browser has a chance to paint.
20 *
21 * ```tsx
22 * function Page() {
23 * const isWide = useMediaQuery('min-width: 1000px')
24 *
25 * return isWide ? "very wide" : 'not so wide'
26 * }
27 * ```
28 *
29 * Media query lists are also reused globally, hook calls for the same query
30 * will only create a matcher once under the hood.
31 *
32 * @param query A media query
33 * @param targetWindow The window to match against, uses the globally available one as a default.
34 */
35export default function useMediaQuery(query, targetWindow = typeof window === 'undefined' ? undefined : window) {
36 const mql = getMatcher(query, targetWindow);
37 const [matches, setMatches] = useState(() => mql ? mql.matches : false);
38 useEffect(() => {
39 let mql = getMatcher(query, targetWindow);
40 if (!mql) {
41 return setMatches(false);
42 }
43 let matchers = matchersByWindow.get(targetWindow);
44 const handleChange = () => {
45 setMatches(mql.matches);
46 };
47 mql.refCount++;
48 mql.addListener(handleChange);
49 handleChange();
50 return () => {
51 mql.removeListener(handleChange);
52 mql.refCount--;
53 if (mql.refCount <= 0) {
54 matchers == null ? void 0 : matchers.delete(mql.media);
55 }
56 mql = undefined;
57 };
58 }, [query]);
59 return matches;
60}
\No newline at end of file