Skip to content

Instantly share code, notes, and snippets.

@saltnpixels
Last active August 16, 2022 20:48
Show Gist options
  • Save saltnpixels/bc383acb77cf0ec9de663bf60fc40d02 to your computer and use it in GitHub Desktop.
Save saltnpixels/bc383acb77cf0ec9de663bf60fc40d02 to your computer and use it in GitHub Desktop.
useSticky
import React, { useEffect, useRef, useState, useCallback } from 'react';
import { throttle } from 'lodash';
/**
* Returns a ref, and a stateful value bound to the ref as well as observe function
* The observe runs automatically on window scroll but you can observe another elements scroll if you want
*/
export default function useSticky<T extends HTMLElement>(fn ?: (stickyActive?: boolean)=> void) {
const stickyRef = useRef<T>(null);
const [sticky, setSticky] = useState(false);
function observe(e?: any) {
if (!stickyRef.current) return;
const refPageOffset = stickyRef.current.getBoundingClientRect().top;
const stickyOffset = parseInt(getComputedStyle(stickyRef.current).top, 10);
const stickyActive = refPageOffset <= stickyOffset;
if (stickyActive) setSticky(true);
else if (!stickyActive) setSticky(false);
if(fn){
fn(stickyActive); // run your own function after observe
}
}
const throttleObserve = throttle(observe, 100);
useEffect(() => {
// Observe when ref enters or leaves sticky state
// rAF https://stackoverflow.com/questions/41740082/scroll-events-requestanimationframe-vs-requestidlecallback-vs-passive-event-lis
throttleObserve();
// Bind events
document.addEventListener('scroll', throttleObserve);
window.addEventListener('resize', throttleObserve);
window.addEventListener('orientationchange', throttleObserve);
return () => {
document.removeEventListener('scroll', throttleObserve);
window.removeEventListener('resize', throttleObserve);
window.removeEventListener('orientationchange', throttleObserve);
};
}, []);
return [stickyRef, sticky, throttleObserve] as const;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment