Created
December 1, 2019 21:06
-
-
Save Ericnr/4dd4f866eaa9c0c9bef3ca4aaf75fb3a to your computer and use it in GitHub Desktop.
Get viewport with debounce
This file contains 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
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