@fluent-windows/hooks
Version:
Fluent-Windows React hooks.
90 lines (82 loc) • 2.07 kB
JavaScript
/**
* Tracks state of a CSS media query.
*
* Demo
* import { useMedia } from '@fluent-windows/hooks'
*
* const isWide = useMedia('(min-width: 600px)')
*
* <div>
* Screen is wide: {isWide ? 'Yes' : 'No'}
* </div>
*
* // or you can use breakpoints
*
* const isWide = useMedia('sm')
* <div>
* Screen is wide: {isWide ? 'Yes' : 'No'}
* </div>
*/
import * as React from 'react';
import * as json2mq_ from 'json2mq';
import useGlobal from '../useGlobal'; // Cannot call a namespace ('json2mq') https://github.com/rollup/rollup/issues/670
// eslint-disable-next-line @typescript-eslint/no-angle-bracket-type-assertion
const json2mq = json2mq_.default || json2mq_;
const defaultBreakpoints = {
sm: 600,
md: 960,
lg: 1280,
xl: 1920
};
const model = ({
sm,
md,
lg,
xl
}) => ({
sm: json2mq({
screen: true,
minWidth: sm
}),
md: json2mq({
screen: true,
minWidth: md
}),
lg: json2mq({
screen: true,
minWidth: lg
}),
xl: json2mq({
screen: true,
minWidth: xl
})
});
function useMedia(mediaQuery, option = {
breakpoints: defaultBreakpoints
}) {
const {
breakpoints
} = option;
const isBreakpoint = Object.prototype.hasOwnProperty.call(breakpoints, mediaQuery);
const transformedModel = React.useMemo(() => model(breakpoints), [breakpoints]);
const global = useGlobal();
const query = isBreakpoint ? transformedModel[mediaQuery] : typeof mediaQuery === 'string' ? mediaQuery : json2mq(mediaQuery);
const mql = global && global.matchMedia && global.matchMedia(query);
if (!mql) return false; // eslint-disable-next-line
const [state, setState] = React.useState(() => mql.matches); // eslint-disable-next-line
React.useLayoutEffect(() => {
let mounted = true;
const handleChange = () => {
if (!mounted) return;
setState(mql.matches);
};
setState(mql.matches);
mql.addListener(handleChange);
return () => {
mounted = false;
mql.removeListener(handleChange);
};
}, [mql, query]);
return state;
}
export default useMedia;