UNPKG

@react-hook/media-query

Version:

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

141 lines (119 loc) 4.74 kB
"use strict"; exports.__esModule = true; exports.useMediaQueries = useMediaQueries; exports.useMediaQuery = useMediaQuery; var React = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("react")); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function queriesDidChange(prevQueries, nextQueries) { if (nextQueries === prevQueries) return false; const nextQueriesArr = Object.values(nextQueries); const prevQueriesArr = Object.values(prevQueries); if (nextQueriesArr.length !== prevQueriesArr.length) return true; if (nextQueriesArr.some((q, i) => q !== prevQueriesArr[i])) return true; const 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) { const queryKeys = Object.keys(queries); /* istanbul ignore next */ if (typeof window === 'undefined') return queryKeys.reduce(_ref, { mediaQueries: {}, matches: {} }); return queryKeys.reduce((state, name) => { const 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) { const prevQueries = React.useRef(queryMap); const [state, dispatch] = React.useReducer(reducer, queryMap, init); React.useEffect(() => { if (queriesDidChange(queryMap, prevQueries.current)) { dispatch({ type: 'setQueries', queries: queryMap }); prevQueries.current = queryMap; } }, [queryMap]); function _ref3() { return dispatch({ type: 'updateMatches' }); } function _ref4(mq) { const callback = _ref3; if (typeof mq.addListener !== 'undefined') mq.addListener(callback);else mq.addEventListener('change', callback); return callback; } React.useEffect(() => { const queries = Object.values(state.mediaQueries); const 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]); const { matches } = state; const matchValues = React.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; } const cache = {}; function getObj(query) { if (cache[query] === void 0) cache[query] = { default: query }; return cache[query]; }