Created
November 1, 2019 05:14
-
-
Save ticidesign/ccbeca5bca00e3df788031accbe31c8d to your computer and use it in GitHub Desktop.
useScroll Hook
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 { useEffect, useState } from 'react'; | |
import ResizeObserver from 'resize-observer-polyfill'; | |
import raf from 'raf-schd'; | |
const LISTENER_OPTIONS = { passive: true }; // we don't call `event.preventDefault()` | |
export function useScroll(ref, { isPassive = true }) { | |
// const scrollTarget = ref.current || document.documentElement; | |
// define setters and getters | |
const [hasScroll, setHasScroll] = useState(false); | |
const [isBottom, setIsBottom] = useState(false); | |
const [isScrollable, setIsScrollable] = useState(false); | |
const [isTop, setIsTop] = useState(false); | |
const [scrollHeight, setScrollHeight] = useState(0); | |
const [scrollTop, setScrollTop] = useState(0); | |
// Called by the resize observer and the scroll listener | |
const handleScroll = raf(({ target }) => { | |
const { clientHeight, scrollHeight: sHeight, scrollTop: sTop } = target; | |
setIsScrollable(sHeight > clientHeight); | |
setScrollHeight(sHeight); | |
setScrollTop(sTop); | |
setIsBottom(sTop === sHeight - clientHeight); | |
setIsTop(sTop === 0); | |
setHasScroll(!!sTop); | |
}); | |
// Add event listeners and observers | |
useEffect(() => { | |
const scrollTarget = ref.current; | |
if (scrollTarget) { | |
if (!isPassive) { | |
scrollTarget.addEventListener('scroll', handleScroll, LISTENER_OPTIONS); | |
} | |
const observer = new ResizeObserver(([entry]) => { | |
handleScroll(entry); | |
}); | |
observer.observe(scrollTarget); | |
// Remove event listeners and disconnect the observer | |
return () => { | |
if (!isPassive) { | |
scrollTarget.removeEventListener('scroll', handleScroll, LISTENER_OPTIONS); | |
} | |
if (observer && scrollTarget) { | |
observer.disconnect(scrollTarget); | |
} | |
}; | |
} | |
}, [ref]); // Empty array ensures that effect is only run on mount and unmount (e.g. doesn't react to prop changes) | |
return { hasScroll, isBottom, isScrollable, isTop, scrollHeight, scrollTop }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment