UNPKG

@react-hook/media-query

Version:

React hooks that update when media queries change between matched and unmatched states.

133 lines (115 loc) 3.69 kB
import { useRef, useReducer, useEffect, useMemo } from 'react'; function queriesDidChange(prevQueries, nextQueries) { if (nextQueries === prevQueries) return false; var nextQueriesArr = Object.values(nextQueries); var prevQueriesArr = Object.values(prevQueries); if (nextQueriesArr.length !== prevQueriesArr.length) return true; if (nextQueriesArr.some((q, i) => q !== prevQueriesArr[i])) return true; var prevKeys = Object.keys(prevQueries); return Object.keys(nextQueries).some((n, i) => n !== prevKeys[i]); } function _ref(curr, key) { curr.matches[key] = false; curr.mediaQueries[key] = {}; return curr; } function init(queries) { var queryKeys = Object.keys(queries); /* istanbul ignore next */ if (typeof window === 'undefined') return queryKeys.reduce(_ref, { mediaQueries: {}, matches: {} }); return queryKeys.reduce((state, name) => { var mql = window.matchMedia(queries[name]); state.mediaQueries[name] = mql; state.matches[name] = mql.matches; return state; }, { mediaQueries: {}, matches: {} }); } function reducer(state, action) { function _ref2(prev, key) { prev[key] = state.mediaQueries[key].matches; return prev; } switch (action.type) { case 'updateMatches': return { matches: Object.keys(state.mediaQueries).reduce(_ref2, {}), mediaQueries: state.mediaQueries }; case 'setQueries': return init(action.queries); } } /** * A hook that returns a [`MediaQueryMatches`](#mediaquerymatches) object which will * tell you if specific media queries matched, all media queries matched, or * any media queries matched. Matches in this hook will always return `false` when * rendering on the server. * * @param queryMap The media queries you want to match against e.g. `{screen: "screen", width: "(min-width: 12em)"}` */ function useMediaQueries(queryMap) { var prevQueries = useRef(queryMap); var [state, dispatch] = useReducer(reducer, queryMap, init); useEffect(() => { if (queriesDidChange(queryMap, prevQueries.current)) { dispatch({ type: 'setQueries', queries: queryMap }); prevQueries.current = queryMap; } }, [queryMap]); function _ref3() { return dispatch({ type: 'updateMatches' }); } function _ref4(mq) { var callback = _ref3; if (typeof mq.addListener !== 'undefined') mq.addListener(callback);else mq.addEventListener('change', callback); return callback; } useEffect(() => { var queries = Object.values(state.mediaQueries); var callbacks = queries.map(_ref4); function _ref5(mq, i) { if (typeof mq.addListener !== 'undefined') mq.removeListener(callbacks[i]);else mq.removeEventListener('change', callbacks[i]); } return () => { queries.forEach(_ref5); }; }, [state.mediaQueries]); var { matches } = state; var matchValues = useMemo(() => Object.values(matches), [matches]); return { matches, matchesAny: matchValues.some(Boolean), matchesAll: matchValues.length > 0 && matchValues.every(Boolean) }; } /** * A hook that returns `true` if the media query matched and `false` if not. This * hook will always return `false` when rendering on the server. * * @param query The media query you want to match against e.g. `"only screen and (min-width: 12em)"` */ function useMediaQuery(query) { return useMediaQueries(getObj(query)).matchesAll; } var cache = {}; function getObj(query) { if (cache[query] === void 0) cache[query] = { default: query }; return cache[query]; } export { useMediaQueries, useMediaQuery }; //# sourceMappingURL=index.dev.mjs.map