Files
automaker/apps/ui/src/hooks/use-media-query.ts
2026-01-12 22:03:14 +01:00

59 lines
1.7 KiB
TypeScript

import { useState, useEffect, useRef } from 'react';
/**
* Hook to detect if a media query matches
* @param query - The media query string (e.g., '(max-width: 768px)')
* @returns boolean indicating if the media query matches
*/
export function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(() => {
if (typeof window === 'undefined') return false;
return window.matchMedia(query).matches;
});
// Track if this is the initial mount to avoid redundant setMatches call
const isInitialMount = useRef(true);
useEffect(() => {
if (typeof window === 'undefined') return;
const mediaQuery = window.matchMedia(query);
const handleChange = (e: MediaQueryListEvent) => {
setMatches(e.matches);
};
// Only sync state when query changes after initial mount
// (initial mount already has correct value from useState initializer)
if (isInitialMount.current) {
isInitialMount.current = false;
} else {
setMatches(mediaQuery.matches);
}
// Listen for changes
mediaQuery.addEventListener('change', handleChange);
return () => {
mediaQuery.removeEventListener('change', handleChange);
};
}, [query]);
return matches;
}
/**
* Hook to detect if the device is mobile (screen width <= 768px)
* @returns boolean indicating if the device is mobile
*/
export function useIsMobile(): boolean {
return useMediaQuery('(max-width: 768px)');
}
/**
* Hook to detect if the device is tablet or smaller (screen width <= 1024px)
* @returns boolean indicating if the device is tablet or smaller
*/
export function useIsTablet(): boolean {
return useMediaQuery('(max-width: 1024px)');
}