45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
/**
|
|
* Saves a feed page's state, with its scroll position, as the page is left.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
|
|
/**
|
|
* Calls `onSave(scrollY)` when the component unmounts or the tab is hidden
|
|
* (`pagehide`), while `enabled`. Feed pages pass `enabled` once their feed is
|
|
* loaded, and an `onSave` that calls `useFeedCache`'s `saveState`.
|
|
*
|
|
* It doesn't save on scroll, since serializing the whole feed there took a long
|
|
* task at every pause.
|
|
*/
|
|
export function useScrollSave(
|
|
enabled: boolean,
|
|
onSave: (scrollY: number) => void,
|
|
): void {
|
|
// A ref, so a new `onSave` on every render doesn't re-attach the listeners.
|
|
const onSaveRef = useRef(onSave);
|
|
useLayoutEffect(() => {
|
|
onSaveRef.current = onSave;
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (!enabled) return;
|
|
// Tracked on scroll: by the time the unmount runs, the next page may have
|
|
// scrolled the window back to the top.
|
|
let y = globalThis.scrollY;
|
|
const onScroll = () => {
|
|
y = globalThis.scrollY;
|
|
};
|
|
const save = () => onSaveRef.current(y);
|
|
globalThis.addEventListener("scroll", onScroll, { passive: true });
|
|
globalThis.addEventListener("pagehide", save);
|
|
return () => {
|
|
globalThis.removeEventListener("scroll", onScroll);
|
|
globalThis.removeEventListener("pagehide", save);
|
|
save();
|
|
};
|
|
}, [enabled]);
|
|
}
|