Skip to content

Commit

Permalink
Better useMediaQuery. useMemo and accurate on initial render. Closes #4
Browse files Browse the repository at this point in the history
  • Loading branch information
jake-figma committed Jun 28, 2024
1 parent ff4f60e commit a00327f
Showing 1 changed file with 39 additions and 51 deletions.
90 changes: 39 additions & 51 deletions src/ui/hooks/useMediaQuery.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,54 @@
import { useEffect, useState } from "react";

type MediaQueryKey =
| "isMobile"
| "isTablet"
| "isDesktop"
| "isTabletUp"
| "isTabletDown";
import { useMemo, useSyncExternalStore } from "react";

const breakpoints = {
mobile: 375,
tablet: 768,
desktop: 1200,
};

const mediaQueries: { [K in MediaQueryKey]: string } = {
isMobile: `(max-width: ${breakpoints.tablet - 1}px)`,
isTablet: `(min-width: ${breakpoints.tablet}px) and (max-width: ${breakpoints.desktop - 1}px)`,
isDesktop: `(min-width: ${breakpoints.desktop}px)`,
isTabletUp: `(min-width: ${breakpoints.tablet}px)`,
isTabletDown: `(max-width: ${breakpoints.desktop - 1}px)`,
};
export function useCustomMediaQuery(mediaQuery: string): boolean {
const [subscribe, getSnapshot] = useMemo(() => {
const mediaQueryList = globalThis.matchMedia(mediaQuery);

const mediaQueryKeys: MediaQueryKey[] = [
"isMobile",
"isTablet",
"isDesktop",
"isTabletUp",
"isTabletDown",
];
function subscribe(onStoreChange: () => void) {
mediaQueryList.addEventListener("change", onStoreChange);
return () => mediaQueryList.removeEventListener("change", onStoreChange);
}

type MediaQueryMatches = { [K in MediaQueryKey]: boolean };
const getSnapshot = () => mediaQueryList.matches;

export const useMediaQuery = () => {
const [matches, setMatches] = useState<MediaQueryMatches>({
isDesktop: false,
isTablet: false,
isTabletDown: true,
isTabletUp: false,
isMobile: true,
});
return [subscribe, getSnapshot];
}, [mediaQuery]);

useEffect(() => {
const matchesLoop = () => {
let changes = false;
const clonedMatches = { ...matches };
for (let key of mediaQueryKeys) {
clonedMatches[key];
const media = window.matchMedia(mediaQueries[key]);
if (media.matches !== clonedMatches[key]) {
clonedMatches[key] = media.matches;
changes = true;
}
}
if (changes) {
setMatches(clonedMatches);
}
return useSyncExternalStore(subscribe, getSnapshot);
}

export const useMediaQuery = () => {
const isMobile = useCustomMediaQuery(
`(max-width: ${breakpoints.tablet - 1}px)`,
);
const isTablet = useCustomMediaQuery(
`(min-width: ${breakpoints.tablet}px) and (max-width: ${breakpoints.desktop - 1}px)`,
);
const isDesktop = useCustomMediaQuery(
`(min-width: ${breakpoints.desktop}px)`,
);
const isTabletUp = useCustomMediaQuery(
`(min-width: ${breakpoints.tablet}px)`,
);
const isTabletDown = useCustomMediaQuery(
`(max-width: ${breakpoints.desktop - 1}px)`,
);

const matches = useMemo(() => {
return {
isMobile,
isTablet,
isDesktop,
isTabletUp,
isTabletDown,
};
matchesLoop();
const listener = () => matchesLoop();
window.addEventListener("resize", listener);
return () => window.removeEventListener("resize", listener);
}, [matches]);
}, [isMobile, isTablet, isDesktop, isTabletUp, isTabletDown]);

return matches;
};

0 comments on commit a00327f

Please sign in to comment.