@react-hookz/web
Version:
React hooks done right, for browser and SSR.
79 lines (78 loc) • 2.8 kB
JavaScript
import { useCallback, useEffect, useRef } from 'react';
import { usePrevious, useRerender } from '..';
import { isBrowser } from "../util/const.js";
var queriesMap = new Map();
var querySubscribe = function (query, setState) {
var entry = queriesMap.get(query);
if (!entry) {
var mql_1 = matchMedia(query);
var dispatchers_1 = new Set();
var listener = function () {
dispatchers_1.forEach(function (d) { return d(mql_1.matches); });
};
if (mql_1.addEventListener)
mql_1.addEventListener('change', listener, { passive: true });
else
mql_1.addListener(listener);
entry = {
mql: mql_1,
dispatchers: dispatchers_1,
listener: listener,
};
queriesMap.set(query, entry);
}
entry.dispatchers.add(setState);
setState(entry.mql.matches, true);
};
var queryUnsubscribe = function (query, setState) {
var entry = queriesMap.get(query);
// else path is impossible to test in normal situation
/* istanbul ignore else */
if (entry) {
var mql = entry.mql, dispatchers = entry.dispatchers, listener = entry.listener;
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.
*
* Defaults to false in SSR environments
*
* @param query CSS media query to track.
* @param matchOnMount whether hook state should be fetched during effects stage instead of
* synchronous fetch. Set this parameter to `true` for SSR use-cases.
*/
export function useMediaQuery(query, matchOnMount) {
var rerender = useRerender();
var previousQuery = usePrevious(query);
var state = useRef();
var setState = useCallback(function (matches, initial) {
if (state.current !== matches) {
state.current = matches;
if (!initial || matchOnMount)
rerender();
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[matchOnMount]);
// do synchronous subscription only for case we are in browser and mount match required
if (!matchOnMount && isBrowser && previousQuery !== query) {
querySubscribe(query, setState);
}
// otherwise, match should happen in effect stage
useEffect(function () {
if (matchOnMount) {
querySubscribe(query, setState);
}
return function () { return queryUnsubscribe(query, setState); };
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [query]);
return state.current;
}