Skip to content

Instantly share code, notes, and snippets.

@Ericnr
Created December 1, 2019 21:06
Show Gist options
  • Save Ericnr/4dd4f866eaa9c0c9bef3ca4aaf75fb3a to your computer and use it in GitHub Desktop.
Save Ericnr/4dd4f866eaa9c0c9bef3ca4aaf75fb3a to your computer and use it in GitHub Desktop.
Get viewport with debounce
import { useState, useRef, useLayoutEffect, useMemo } from 'react';
function getVpWidth() {
return typeof window !== 'undefined'
? Math.max(
window.document.documentElement.clientWidth,
window.innerWidth || 0
)
: 0;
}
function getVpHeight() {
return typeof window !== 'undefined'
? Math.max(
window.document.documentElement.clientHeight,
window.innerHeight || 0
)
: 0;
}
// =============== //
// Shared State //
// =============== //
// using separate variables since Babel
// transpilation saves a bit of filesize
const listeners = new Set();
let vpW = getVpWidth();
let vpH = getVpHeight();
// should only be called by *one* component once;
// will iterate through all subscribers
// afterwards
function onResize() {
vpW = getVpWidth();
vpH = getVpHeight();
listeners.forEach((listener: any) => {
listener({ vpWidth: vpW, vpHeight: vpH });
});
}
// =============== //
// the Hook //
// =============== //
/**
* Returns the window dimensions at real time.
*
* @param {number} debounce Optional, in case the component consuming the hook is too expensive to render.
* Will register the dimensions only after the user has stopped resizing after the specified amount of debounce time.
*/
function useViewportSizes(debounce: number): [number, number, () => void] {
const [{ vpWidth, vpHeight }, setState] = useState(() => ({
vpWidth: vpW,
vpHeight: vpH
}));
const timeout = useRef<undefined | number>(undefined);
const listener = useMemo(
() =>
!debounce
? (state: any) => setState(state)
: (state: any) => {
if (timeout.current) {
clearTimeout(timeout.current);
}
timeout.current = setTimeout(() => setState(state), debounce);
},
[debounce, setState]
);
useLayoutEffect(() => {
listeners.add(listener);
if (window && listeners.size === 1) {
window.addEventListener('resize', onResize);
onResize();
}
// clean up listeners on unmount
return () => {
listeners.delete(listener);
if (listeners.size === 0) {
window.removeEventListener('resize', onResize);
}
};
}, [listener]);
return [vpWidth, vpHeight, onResize];
}
export default useViewportSizes;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment