Created
December 23, 2019 03:19
-
-
Save maraisr/c4681874646913efc34d5867255a4612 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable react-hooks/rules-of-hooks */ | |
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; | |
import { unstable_batchedUpdates } from 'react-dom'; | |
import { useOverdriveContext } from '../components/OverdriveProvider'; | |
import { useTheme } from '../components/ThemeProvider'; | |
export const useMedia = ( | |
queries: ReadonlyArray<keyof Theme['breakpoints']>, | |
fallbackCase = false, | |
): readonly boolean[] => { | |
const theme = useTheme(); | |
const { isServer } = useOverdriveContext(); | |
if (isServer) return queries.map(() => fallbackCase); | |
const getQueries = useCallback(() => queries.map(media => makeQueryString(...theme.breakpoints[media])), [theme]); | |
const matchesInit = useMemo(() => getQueries().map(query => window.matchMedia(query).matches), [getQueries]); | |
const [matches, setMatches] = useState<readonly boolean[]>(matchesInit); | |
const handler = individualHandler(setMatches); | |
useLayoutEffect(() => { | |
let isMounted = true; | |
const constructedQueries = getQueries(); | |
const removeHandlers = constructedQueries | |
.map((query, idx) => | |
addMatchMedia(query, matches => { | |
isMounted && handler(matches, idx); | |
})); | |
return () => { | |
isMounted = false; | |
removeHandlers.forEach(handle => handle()); | |
}; | |
}, [getQueries, handler, theme]); | |
return matches; | |
}; | |
const medias = new Map<string, { handlers: Array<(matches: boolean) => void>; matcher: MediaQueryList }>(); | |
const globalMatchMediaHandler = (e: MediaQueryListEvent) => { | |
unstable_batchedUpdates(() => { | |
medias.get(e.media)?.handlers | |
.forEach(handle => { | |
handle(e.matches); | |
}); | |
}); | |
}; | |
const addMatchMedia = (query: string, handler) => { | |
if (medias.has(query)) { | |
const media = medias.get(query); | |
media.handlers.push(handler); | |
medias.set(query, media); | |
} else { | |
const matcher = window.matchMedia(query); | |
matcher.addListener(globalMatchMediaHandler); | |
medias.set(query, { | |
matcher, | |
handlers: [handler], | |
}); | |
} | |
return () => void removeMatchMedia(query, handler); | |
}; | |
const removeMatchMedia = (query: string, handler) => { | |
const media = medias.get(query); | |
const newHandlers = media.handlers.filter(item => item !== handler); | |
if (newHandlers.length === 0) { | |
media.matcher.removeListener(globalMatchMediaHandler); | |
medias.delete(query); | |
return; | |
} | |
medias.set(query, { | |
matcher: media.matcher, | |
handlers: newHandlers, | |
}); | |
}; | |
const individualHandler = setMatches => (matches, index) => void setMatches(prev => { | |
const newColl = prev.slice(); | |
newColl[index] = matches; | |
return newColl; | |
}); | |
const makeQueryString = (min, max = null) => { | |
const hasMax = max !== null; | |
return `screen and (min-width: ${min}px${ | |
hasMax ? ` and max-width: ${max - 1}px` : '' | |
})`; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment