Skip to content

Instantly share code, notes, and snippets.

@tokdaniel
Last active November 19, 2019 00:02
Show Gist options
  • Save tokdaniel/39e3d2a4a17581ed4e1e771e111532f8 to your computer and use it in GitHub Desktop.
Save tokdaniel/39e3d2a4a17581ed4e1e771e111532f8 to your computer and use it in GitHub Desktop.
import { useState, useEffect, useRef, EffectCallback } from 'react';
import throttle from 'lodash/throttle';
import debounce from 'lodash/debounce';
// Ties two states together, and tracks only if a specific case occur.
export const transitionTracker = <A, B>(from: A, to: B) => {
let marker = 0;
return (state1: A, state2: B) => {
if (from === state1 && to === state2) {
marker = marker + 1;
}
return marker;
};
};
type ScrollY = number;
type UseScrollYHook = (
updateOptions?: {
limit?: number;
method?: 'THROTTLE' | 'DEBOUNCE';
leading?: boolean;
trailing?: boolean;
}) => ScrollY;
//Provides the actual scrollY number with options to throttle, or debounce the value updates.
export const useScrollY: UseScrollYHook = (options) => {
const [scrollY, registerScrollY] = useState(window.scrollY);
const { limit, method, leading, trailing } = options;
let update = () => {
registerScrollY(window.scrollY);
};
if (limit) {
if (method === 'THROTTLE') {
update = throttle(update, limit, { leading, trailing });
} else if (method === 'DEBOUNCE') {
update = debounce(update, limit, { leading, trailing });
}
}
useEffect(() => {
window.addEventListener('scroll', update);
return function cleanup() {
window.removeEventListener('scroll', update);
};
}, []);
return scrollY;
};
// Save the state of something in a previous render cycle.
export const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
};
// track the transition when wasScrolledToBottom is FALSE and isScrolledToBottom is TRUE only.
const track = transitionTracker<boolean, boolean>(false, true);
// Triggers an effect, when the page was NOT SCROLLED to BOTTOM at the previous render and it
// IS SCROLLED to BOTTOM currently.
export const triggerEffectAtBottom = (
effect: EffectCallback,
ignoreFirst = true,
offsetBottom = 0,
) => {
const scrollY = useScrollY({
limit: 50,
method: 'THROTTLE',
leading: true,
});
const scrollableDist = document.body.scrollHeight - window.innerHeight;
const isScrolledToBottom = scrollY - offsetBottom === scrollableDist;
const wasScrolledToBottom = usePrevious(isScrolledToBottom);
const isFirstRun = useRef(true);
useEffect(() => {
if (isFirstRun.current && ignoreFirst) {
isFirstRun.current = false;
return;
} else if (isScrolledToBottom && !wasScrolledToBottom) {
effect();
}
}, [track(wasScrolledToBottom, isScrolledToBottom)]);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment