@modern-kit/react
Version:
1 lines • 6.78 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../../src/hooks/useIntersectionObserver/index.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\nimport { usePreservedCallback } from '../usePreservedCallback';\nimport { noop } from '@modern-kit/utils';\n\nexport interface UseIntersectionObserverProps extends IntersectionObserverInit {\n onIntersectStart?: (entry: IntersectionObserverEntry) => void;\n onIntersectEnd?: (entry: IntersectionObserverEntry) => void;\n calledOnce?: boolean;\n enabled?: boolean;\n}\n\ninterface UseIntersectionObserverReturnType<T extends HTMLElement> {\n ref: React.RefCallback<T>;\n isIntersecting: boolean;\n hasIntersected: boolean;\n}\n\n/**\n * @description `useIntersectionObserver` 훅은 주어진 타겟 요소가 뷰포트(viewport) 내에 들어오거나 나가는지 관찰하기 위한 Intersection Observer를 설정합니다.\n *\n * 이 훅은 요소가 교차할 때 호출할 콜백을 제공하며, 한 번만 호출되거나 반복적으로 호출되도록 설정할 수 있습니다.\n *\n * @template T - 관찰할 HTML 요소 타입, 기본적으로 `HTMLElement`를 상속합니다.\n *\n * @param {(entry: IntersectionObserverEntry) => void} params.onIntersectStart - 타겟 요소가 viewport 내에 들어갈 때 호출되는 콜백 함수입니다.\n * @param {(entry: IntersectionObserverEntry) => void} params.onIntersectEnd - 타겟 요소가 viewport에서 나갈 때 호출되는 콜백 함수입니다.\n * @param {boolean} [params.enabled=true] - Observer를 활성화할지 여부를 나타냅니다. `false`일 경우 Observer가 작동하지 않습니다.\n * @param {boolean} [params.calledOnce=false] - 요소가 교차할 때 콜백을 `한 번`만 호출할지 여부를 나타냅니다.\n * @param {Element} [params.root=null] - 교차할 때 기준이 되는 root 요소입니다. 기본값은 `null`이며 이는 viewport를 의미합니다.\n * @param {number | number[]} [params.threshold=0] - Observer가 콜백을 호출하는 임계값을 나타냅니다.\n * @param {string} [params.rootMargin='0px 0px 0px 0px'] - 루트 요소에 대한 마진을 지정합니다. 이는 뷰포트 또는 루트 요소의 경계를 확장하거나 축소하는데 사용됩니다.\n *\n * @returns {UseIntersectionObserverReturnType<T>} ref를 포함한 isIntersecting과 hasIntersected 값을 반환합니다.\n * - `ref`: 관찰할 타겟 요소에 전달할 ref\n * - `isIntersecting`: 타겟 요소가 교차하는지 여부를 나타내는 boolean 값\n * - `hasIntersected`: 타겟 요소가 최초로 교차했는지 여부를 나타내는 boolean 값\n *\n * @example\n * ```tsx\n * const { ref: targetRef, isIntersecting, hasIntersected } = useIntersectionObserver<HTMLDivElement>({\n * onIntersectStart: () => console.log('onIntersectStart'),\n * onIntersectEnd: () => console.log('onIntersectEnd'),\n * calledOnce: true,\n * enabled: true,\n * ...intersectionObserverOptions, // root, threshold, rootMargin\n * });\n *\n * <div ref={targetRef} />\n * ```\n */\nexport function useIntersectionObserver<T extends HTMLElement>({\n onIntersectStart = noop,\n onIntersectEnd = noop,\n enabled = true,\n calledOnce = false,\n root = null,\n threshold = 0,\n rootMargin = '0px 0px 0px 0px',\n}: UseIntersectionObserverProps = {}): UseIntersectionObserverReturnType<T> {\n const [isIntersecting, setIsIntersecting] = useState(false);\n const [hasIntersected, setHasIntersected] = useState(false);\n\n const observerRef = useRef<IntersectionObserver | null>(null);\n const calledCount = useRef(0);\n\n const intersectionObserverCallback = usePreservedCallback(\n ([entry]: IntersectionObserverEntry[], observer: IntersectionObserver) => {\n if (!entry) return;\n\n const targetElement = entry.target as T;\n setIsIntersecting(entry.isIntersecting);\n\n if (entry.isIntersecting) {\n calledCount.current += 1;\n\n setHasIntersected(true);\n onIntersectStart(entry);\n } else if (isIntersecting) {\n // 최초 mount 시에 호출을 방지하고, 타겟 요소가 viewport에서 나갈 때만 호출\n calledCount.current += 1;\n\n onIntersectEnd(entry);\n }\n\n if (calledOnce && calledCount.current > 1) {\n observer.unobserve(targetElement);\n }\n }\n );\n\n const targetRef = useCallback(\n (node: T) => {\n // 기존 observer가 활성화된 상태에서 새로운 요소를 관찰하기 전에 기존 observer 관찰 중지하며, 메모리 누수를 방지\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n\n if (node == null || !enabled) return;\n\n observerRef.current = new IntersectionObserver(\n intersectionObserverCallback,\n {\n threshold,\n root,\n rootMargin,\n }\n );\n observerRef.current.observe(node);\n },\n [enabled, threshold, root, rootMargin, intersectionObserverCallback]\n );\n\n return { ref: targetRef, isIntersecting, hasIntersected };\n}\n"],"names":["noop","useState","useRef","usePreservedCallback","useCallback"],"mappings":";;;;;;AAkDO,SAAS,uBAAA,CAA+C;AAAA,EAC7D,gBAAA,GAAmBA,UAAA;AAAA,EACnB,cAAA,GAAiBA,UAAA;AAAA,EACjB,OAAA,GAAU,IAAA;AAAA,EACV,UAAA,GAAa,KAAA;AAAA,EACb,IAAA,GAAO,IAAA;AAAA,EACP,SAAA,GAAY,CAAA;AAAA,EACZ,UAAA,GAAa;AACf,CAAA,GAAkC,EAAC,EAAyC;AAC1E,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIC,eAAS,KAAK,CAAA;AAC1D,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIA,eAAS,KAAK,CAAA;AAE1D,EAAA,MAAM,WAAA,GAAcC,aAAoC,IAAI,CAAA;AAC5D,EAAA,MAAM,WAAA,GAAcA,aAAO,CAAC,CAAA;AAE5B,EAAA,MAAM,4BAAA,GAA+BC,8CAAA;AAAA,IACnC,CAAC,CAAC,KAAK,CAAA,EAAgC,QAAA,KAAmC;AACxE,MAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,MAAA,MAAM,gBAAgB,KAAA,CAAM,MAAA;AAC5B,MAAA,iBAAA,CAAkB,MAAM,cAAc,CAAA;AAEtC,MAAA,IAAI,MAAM,cAAA,EAAgB;AACxB,QAAA,WAAA,CAAY,OAAA,IAAW,CAAA;AAEvB,QAAA,iBAAA,CAAkB,IAAI,CAAA;AACtB,QAAA,gBAAA,CAAiB,KAAK,CAAA;AAAA,MACxB,WAAW,cAAA,EAAgB;AAEzB,QAAA,WAAA,CAAY,OAAA,IAAW,CAAA;AAEvB,QAAA,cAAA,CAAe,KAAK,CAAA;AAAA,MACtB;AAEA,MAAA,IAAI,UAAA,IAAc,WAAA,CAAY,OAAA,GAAU,CAAA,EAAG;AACzC,QAAA,QAAA,CAAS,UAAU,aAAa,CAAA;AAAA,MAClC;AAAA,IACF;AAAA,GACF;AAEA,EAAA,MAAM,SAAA,GAAYC,iBAAA;AAAA,IAChB,CAAC,IAAA,KAAY;AAEX,MAAA,IAAI,YAAY,OAAA,EAAS;AACvB,QAAA,WAAA,CAAY,QAAQ,UAAA,EAAW;AAC/B,QAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AAAA,MACxB;AAEA,MAAA,IAAI,IAAA,IAAQ,IAAA,IAAQ,CAAC,OAAA,EAAS;AAE9B,MAAA,WAAA,CAAY,UAAU,IAAI,oBAAA;AAAA,QACxB,4BAAA;AAAA,QACA;AAAA,UACE,SAAA;AAAA,UACA,IAAA;AAAA,UACA;AAAA;AACF,OACF;AACA,MAAA,WAAA,CAAY,OAAA,CAAQ,QAAQ,IAAI,CAAA;AAAA,IAClC,CAAA;AAAA,IACA,CAAC,OAAA,EAAS,SAAA,EAAW,IAAA,EAAM,YAAY,4BAA4B;AAAA,GACrE;AAEA,EAAA,OAAO,EAAE,GAAA,EAAK,SAAA,EAAW,cAAA,EAAgB,cAAA,EAAe;AAC1D;;;;"}