@react-hookz/web
Version:
React hooks done right, for browser and SSR.
58 lines (57 loc) • 1.76 kB
JavaScript
import { useEffect } from 'react';
import { useSafeState } from '..';
const queriesMap = new Map();
const querySubscribe = (query, setState) => {
let entry = queriesMap.get(query);
if (!entry) {
const mql = matchMedia(query);
const dispatchers = new Set();
const listener = () => {
dispatchers.forEach((d) => d(mql.matches));
};
if (mql.addEventListener)
mql.addEventListener('change', listener, { passive: true });
else
mql.addListener(listener);
entry = {
mql,
dispatchers,
listener,
};
queriesMap.set(query, entry);
}
entry.dispatchers.add(setState);
setState(entry.mql.matches);
};
const queryUnsubscribe = (query, setState) => {
const entry = queriesMap.get(query);
// else path is impossible to test in normal situation
/* istanbul ignore else */
if (entry) {
const { mql, dispatchers, listener } = entry;
dispatchers.delete(setState);
if (!dispatchers.size) {
queriesMap.delete(query);
if (mql.removeEventListener)
mql.removeEventListener('change', listener);
else
mql.removeListener(listener);
}
}
};
/**
* Tracks the state of CSS media query.
*
* Return undefined initially and later receives correct value.
*
* @param query CSS media query to track.
*/
export function useMediaQuery(query) {
const [state, setState] = useSafeState();
useEffect(() => {
querySubscribe(query, setState);
return () => queryUnsubscribe(query, setState);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [query]);
return state;
}