Skip to content

Instantly share code, notes, and snippets.

@iDVB
Created April 21, 2020 19:23
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iDVB/a041da210605f05e0b36ac03ed403c00 to your computer and use it in GitHub Desktop.
Save iDVB/a041da210605f05e0b36ac03ed403c00 to your computer and use it in GitHub Desktop.
useScrollSpy similar to MUI docs https://bit.ly/3cArBBr
import { useState, useEffect, useRef, useCallback } from 'react';
import useThrottledOnScroll from './useThrottledOnScroll';
const useScrollSpy = ({ items = [], target = window } = {}) => {
const itemsWithNodeRef = useRef([]);
useEffect(() => {
itemsWithNodeRef.current = getItemsClient(items);
}, [items]);
const [activeState, setActiveState] = useState(null);
const findActiveIndex = useCallback(() => {
let active;
for (let i = itemsWithNodeRef.current.length - 1; i >= 0; i -= 1) {
// No hash if we're near the top of the page
if (document.documentElement.scrollTop < 200) {
active = { hash: null };
break;
}
const item = itemsWithNodeRef.current[i];
if (process.env.NODE_ENV !== 'production') {
if (!item.node) {
console.error(
`Missing node on the item ${JSON.stringify(item, null, 2)}`
);
}
}
if (
item.node &&
item.node.offsetTop <
document.documentElement.scrollTop +
document.documentElement.clientHeight / 8
) {
active = item;
break;
}
}
if (active && activeState !== active.hash) {
setActiveState(active.hash);
}
}, [activeState]);
useThrottledOnScroll(items.length > 0 ? findActiveIndex : null, 166);
return activeState;
};
const getItemsClient = items =>
items.map(({ hash }) => ({ hash, node: document.getElementById(hash) }));
export default useScrollSpy;
import { useEffect, useMemo } from 'react';
import throttle from 'lodash/throttle';
const useThrottledOnScroll = (callback, delay) => {
const throttledCallback = useMemo(
() => (callback ? throttle(callback, delay) : noop),
[callback, delay]
);
useEffect(() => {
if (throttledCallback === noop) {
return undefined;
}
window.addEventListener('scroll', throttledCallback);
return () => {
window.removeEventListener('scroll', throttledCallback);
throttledCallback.cancel();
};
}, [throttledCallback]);
};
const noop = () => {};
export default useThrottledOnScroll;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment