@barguide/react-hooks
Version:
TypeScript | React Hooks
82 lines (66 loc) • 2.13 kB
text/typescript
import { useEffect, useState } from 'react';
import throttle from 'lodash/throttle';
/**
* @name useCheckoutLoader
* @description We use the "effect" hook to add/remove an "emitter" instance
* to our Modal component. By passing the "setter" from our React State we
* can get a live subscription to the state
*/
const useScrollDirection = () => {
const threshhold = 30;
const triggerBottom = 100;
const triggerTop = 100;
const triggerDistance = 150;
const [bottom, setBottom] = useState(false);
const [direction, setDirection] = useState(false);
const [position, setPosition] = useState(0);
const [top, setTop] = useState(false);
const onScroll = (_event: any) => {
const root = document.documentElement;
const { clientHeight, scrollTop, scrollHeight } = root;
const remainder = scrollHeight - clientHeight - scrollTop;
const isBottom = remainder <= triggerBottom;
const isTop = scrollTop <= threshhold;
const isSignificant = Math.abs(scrollTop - position) > triggerDistance;
const nearBottom = remainder <= triggerBottom;
const nearTop = scrollTop <= triggerTop;
const isException = nearBottom || nearTop;
if (isException) {
if (nearTop && !top) {
setBottom(false);
setTop(true);
} else if (nearBottom && !bottom) {
setBottom(true);
setTop(false);
}
return;
}
// Required travel distance
if (isSignificant) {
const isDownward = scrollTop > position;
// Edge-cases
if (top !== isTop) setTop(isTop);
else if (bottom !== isBottom) setBottom(isBottom);
// Account for negative scrolling (mobile)
setPosition(scrollTop <= 0 ? 0 : scrollTop);
setDirection(isDownward);
}
};
const throttledResize = throttle(onScroll, 100, {
leading: true,
trailing: true
});
useEffect(() => {
window.addEventListener('scroll', throttledResize);
return () => {
window.removeEventListener('scroll', throttledResize);
};
});
return {
bottom,
direction,
position,
top
};
};
export { useScrollDirection };