withSticky.utils.ts53 lines · main
1import { useCallback, useRef } from 'react'
2import { useInView } from 'react-intersection-observer'
3
4const useSticky = <Element extends HTMLElement>({
5 enabled = true,
6 style = {} as CSSStyleDeclaration,
7}: {
8 enabled?: boolean
9 style?: CSSStyleDeclaration
10} = {}) => {
11 const stickyRef = useRef<Element>(null)
12
13 const handleSticking = useCallback(
14 (inView: boolean) => {
15 if (!stickyRef.current) return
16
17 if (inView) {
18 stickyRef.current.style.position = 'sticky'
19 stickyRef.current.style.top = '100px'
20 stickyRef.current.style.zIndex = '5'
21
22 for (const property in style) {
23 // @ts-ignore
24 stickyRef.current.style[property] = style[property]
25 }
26 } else {
27 stickyRef.current.style.position = ''
28 stickyRef.current.style.top = ''
29 stickyRef.current.style.zIndex = ''
30 for (const property in style) {
31 // @ts-ignore
32 stickyRef.current.style[property] = ''
33 }
34 }
35 },
36 // eslint-disable-next-line react-hooks/exhaustive-deps
37 [JSON.stringify(style)]
38 )
39
40 const { ref: observedRef, inView } = useInView({
41 threshold: 0.1,
42 onChange: handleSticking,
43 skip: !enabled,
44 })
45
46 return {
47 inView,
48 observedRef,
49 stickyRef,
50 }
51}
52
53export { useSticky }