@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
JavaScript
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