Files
explore/lib/contexts/scrollDirection.tsx
T
2022-11-30 16:35:45 +03:00

59 lines
1.8 KiB
TypeScript

import clamp from 'lodash/clamp';
import throttle from 'lodash/throttle';
import React from 'react';
const ScrollDirectionContext = React.createContext<'up' | 'down' | null>(null);
import isBrowser from 'lib/isBrowser';
const SCROLL_DIFF_THRESHOLD = 20;
type Directions = 'up' | 'down';
interface Props {
children: React.ReactNode;
}
export function ScrollDirectionProvider({ children }: Props) {
const prevScrollPosition = React.useRef(isBrowser() ? window.pageYOffset : 0);
const [ scrollDirection, setDirection ] = React.useState<Directions | null>(null);
const handleScroll = React.useCallback(() => {
const currentScrollPosition = clamp(window.pageYOffset, 0, window.document.body.scrollHeight - window.innerHeight);
const scrollDiff = currentScrollPosition - prevScrollPosition.current;
if (window.pageYOffset === 0) {
setDirection(null);
} else if (Math.abs(scrollDiff) > SCROLL_DIFF_THRESHOLD) {
setDirection(scrollDiff < 0 ? 'up' : 'down');
}
prevScrollPosition.current = currentScrollPosition;
}, [ ]);
React.useEffect(() => {
const throttledHandleScroll = throttle(handleScroll, 300);
window.addEventListener('scroll', throttledHandleScroll);
return () => {
window.removeEventListener('scroll', throttledHandleScroll);
};
// replicate componentDidMount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<ScrollDirectionContext.Provider value={ scrollDirection }>
{ children }
</ScrollDirectionContext.Provider>
);
}
export function useScrollDirection() {
const context = React.useContext(ScrollDirectionContext);
if (context === undefined) {
throw new Error('useScrollDirection must be used within a ScrollDirectionProvider');
}
return context;
}