UNPKG

@react-hook/media-query

Version:

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

119 lines (103 loc) 2.99 kB
import React from 'react' const {useRef, useMemo, useEffect, useReducer} = React const 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 } const 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) } } export const useMediaQueries = queries => { const prevQueries = useRef(queries) const [state, dispatch] = useReducer(reducer, queries, init) useEffect(() => { if (queriesDidChange(queries, prevQueries.current)) { dispatch({ type: 'setQueries', queries, }) prevQueries.current = queries } }, [queries]) 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 } 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 = useMemo(() => Object.values(matches), [matches]) return { matches, matchesAny: matchValues.some(Boolean), matchesAll: matchValues.length > 0 && matchValues.every(Boolean), } } const cache = {} const getObj = query => { if (cache[query] === void 0) cache[query] = { default: query, } return cache[query] } export const useMediaQuery = query => useMediaQueries(getObj(query)).matchesAll