Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | import React, { useCallback, useEffect, useRef } from "react"; const scrollCache: Record<string, number> = {}; interface CachedScrollProps { children: React.ReactNode; cacheKey: string; } const CachedScroll = ({ children, cacheKey }: CachedScrollProps) => { const scrollRef = useRef<HTMLDivElement>(null); const isProgrammaticScroll = useRef(true); const isUserScrolling = useRef(false); const onScroll = useCallback(() => { Iif (isProgrammaticScroll.current) { isProgrammaticScroll.current = false; return; } Iif (scrollRef.current) { // Was user scroll so update cached scroll pos and cancel auto scrolling scrollCache[cacheKey] = scrollRef.current.scrollTop; isUserScrolling.current = true; } }, [cacheKey]); useEffect(() => { const checkScroll = () => { const savedScrollPosition = scrollCache[cacheKey]; const savedPosition = savedScrollPosition ?? 0; Iif (scrollRef.current) { // If not reached saved pos yet Iif ( !isUserScrolling.current && scrollRef.current.scrollTop < savedPosition ) { // Only scroll if there is more scroll height available // to prevent clash with user initiated scroll events Iif ( scrollRef.current.scrollTop < scrollRef.current.scrollHeight - scrollRef.current.clientHeight ) { isProgrammaticScroll.current = true; scrollRef.current.scrollTop = savedPosition; } requestAnimationFrame(checkScroll); } } }; const savedScrollPosition = scrollCache[cacheKey]; const savedPosition = savedScrollPosition ?? 0; Iif (savedPosition > 0 && scrollRef.current) { scrollRef.current.scrollTop = savedPosition; requestAnimationFrame(checkScroll); } }, [cacheKey]); return ( <div ref={scrollRef} onScroll={onScroll} style={{ width: "100%", height: "100%", overflowY: "auto" }} > {children} </div> ); }; export default CachedScroll; |