DeepBrowsingProvider.tsx raw

   1  import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'
   2  
   3  type TDeepBrowsingContext = {
   4    deepBrowsing: boolean
   5    lastScrollTop: number
   6  }
   7  
   8  const DeepBrowsingContext = createContext<TDeepBrowsingContext | undefined>(undefined)
   9  
  10  export const useDeepBrowsing = () => {
  11    const context = useContext(DeepBrowsingContext)
  12    if (!context) {
  13      throw new Error('useDeepBrowsing must be used within a DeepBrowsingProvider')
  14    }
  15    return context
  16  }
  17  
  18  export function DeepBrowsingProvider({
  19    children,
  20    active,
  21    scrollAreaRef
  22  }: {
  23    children: React.ReactNode
  24    active: boolean
  25    scrollAreaRef?: React.RefObject<HTMLDivElement>
  26  }) {
  27    const [deepBrowsing, setDeepBrowsing] = useState(false)
  28    const lastScrollTopRef = useRef(
  29      (!scrollAreaRef ? window.scrollY : scrollAreaRef.current?.scrollTop) || 0
  30    )
  31    const [lastScrollTop, setLastScrollTop] = useState(lastScrollTopRef.current)
  32    const rafRef = useRef(0)
  33  
  34    const handleScroll = useCallback(() => {
  35      if (rafRef.current) return
  36      rafRef.current = requestAnimationFrame(() => {
  37        rafRef.current = 0
  38        const scrollTop = (!scrollAreaRef ? window.scrollY : scrollAreaRef.current?.scrollTop) || 0
  39        const diff = scrollTop - lastScrollTopRef.current
  40        lastScrollTopRef.current = scrollTop
  41        setLastScrollTop(scrollTop)
  42        if (scrollTop <= 800) {
  43          setDeepBrowsing(false)
  44          return
  45        }
  46  
  47        if (diff > 20) {
  48          setDeepBrowsing(true)
  49        } else if (diff < -20) {
  50          setDeepBrowsing(false)
  51        }
  52      })
  53    }, [scrollAreaRef])
  54  
  55    useEffect(() => {
  56      if (!active) return
  57  
  58      const target = scrollAreaRef ? scrollAreaRef.current : window
  59  
  60      target?.addEventListener('scroll', handleScroll)
  61      return () => {
  62        target?.removeEventListener('scroll', handleScroll)
  63        if (rafRef.current) {
  64          cancelAnimationFrame(rafRef.current)
  65          rafRef.current = 0
  66        }
  67      }
  68    }, [active, handleScroll])
  69  
  70    return (
  71      <DeepBrowsingContext.Provider value={{ deepBrowsing, lastScrollTop }}>
  72        {children}
  73      </DeepBrowsingContext.Provider>
  74    )
  75  }
  76