fix(08-02): scroll restoration — save/restore both window and <main> scrollTop, defer to rAF after rows render
This commit is contained in:
parent
6bdc937861
commit
8834db981d
1 changed files with 29 additions and 16 deletions
|
|
@ -172,35 +172,48 @@ export default function MobileEngagementPage() {
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sortKey]);
|
||||
|
||||
// D-04 fallback: the mobile shell's <main> is overflow-y-auto, so Next.js's
|
||||
// built-in scrollRestoration (which targets window) doesn't help. Persist the
|
||||
// inner scroll position across navigations via sessionStorage so back from
|
||||
// /mobile/engagement/[userId] restores the row the user tapped.
|
||||
// D-04 fallback: scroll restoration via sessionStorage. The actual scroll
|
||||
// container differs by viewport — `<main>` has overflow-y-auto, but in some
|
||||
// configurations the document scrolls instead. Listen on both, save the one
|
||||
// that's non-zero, and restore both.
|
||||
const restoredScrollRef = useRef(false);
|
||||
useEffect(() => {
|
||||
const main = document.querySelector('main');
|
||||
if (!main) return;
|
||||
let raf = 0;
|
||||
const save = () => {
|
||||
const main = document.querySelector('main');
|
||||
const mainTop = main?.scrollTop ?? 0;
|
||||
const winTop = window.scrollY || document.documentElement.scrollTop || 0;
|
||||
sessionStorage.setItem('mobile-engagement-scroll', JSON.stringify({ main: mainTop, win: winTop }));
|
||||
};
|
||||
const onScroll = () => {
|
||||
if (raf) return;
|
||||
raf = requestAnimationFrame(() => {
|
||||
raf = 0;
|
||||
sessionStorage.setItem('mobile-engagement-list-scroll', String(main.scrollTop));
|
||||
});
|
||||
raf = requestAnimationFrame(() => { raf = 0; save(); });
|
||||
};
|
||||
main.addEventListener('scroll', onScroll, { passive: true });
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
const main = document.querySelector('main');
|
||||
main?.addEventListener('scroll', onScroll, { passive: true });
|
||||
return () => {
|
||||
main.removeEventListener('scroll', onScroll);
|
||||
window.removeEventListener('scroll', onScroll);
|
||||
main?.removeEventListener('scroll', onScroll);
|
||||
if (raf) cancelAnimationFrame(raf);
|
||||
};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (restoredScrollRef.current) return;
|
||||
if (usersLoading || users.length === 0) return;
|
||||
const main = document.querySelector('main');
|
||||
if (!main) return;
|
||||
const saved = sessionStorage.getItem('mobile-engagement-list-scroll');
|
||||
if (saved) main.scrollTop = parseInt(saved, 10);
|
||||
const raw = sessionStorage.getItem('mobile-engagement-scroll');
|
||||
if (!raw) { restoredScrollRef.current = true; return; }
|
||||
try {
|
||||
const { main: mainTop, win: winTop } = JSON.parse(raw) as { main: number; win: number };
|
||||
// Defer one frame so layout has settled with the new rows in place.
|
||||
requestAnimationFrame(() => {
|
||||
const main = document.querySelector('main');
|
||||
if (main && mainTop) main.scrollTop = mainTop;
|
||||
if (winTop) window.scrollTo(0, winTop);
|
||||
});
|
||||
} catch {
|
||||
/* corrupt entry — ignore */
|
||||
}
|
||||
restoredScrollRef.current = true;
|
||||
}, [usersLoading, users.length]);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue