diff --git a/.changeset/gold-cheetahs-clap.md b/.changeset/gold-cheetahs-clap.md new file mode 100644 index 000000000..36c073cce --- /dev/null +++ b/.changeset/gold-cheetahs-clap.md @@ -0,0 +1,5 @@ +--- +'@modern-kit/react': patch +--- + +fix: useIntersectionObserver 기능 개선 diff --git a/docs/docs/react/components/InView.mdx b/docs/docs/react/components/InView.mdx index 8ba552422..b24effc89 100644 --- a/docs/docs/react/components/InView.mdx +++ b/docs/docs/react/components/InView.mdx @@ -8,14 +8,6 @@ import { InView } from '@modern-kit/react'; ## Interface ```tsx -interface UseIntersectionObserverProps { - action: (entry: IntersectionObserverEntry) => void; - calledOnce?: boolean; - threshold?: number | number[]; - root?: Document | Element | null; - rootMargin?: string; -} - type InViewProps = React.ComponentProps<'div'> & UseIntersectionObserverProps; const InView: React.ForwardRefExoticComponent< diff --git a/docs/docs/react/components/LazyImage.mdx b/docs/docs/react/components/LazyImage.mdx index 95024047d..378d24ac7 100644 --- a/docs/docs/react/components/LazyImage.mdx +++ b/docs/docs/react/components/LazyImage.mdx @@ -12,14 +12,13 @@ Intersection Observer Option을 설정할 수 있습니다.(하단 `Note` 참고 ## Interface ```tsx -interface LazyImageProps extends React.ComponentProps<'img'> { +interface LazyImageProps + extends React.ComponentProps<'img'>, + IntersectionObserverInit { src: string; - threshold?: number | number[]; // default: 0 - root?: Document | Element | null; // default: null - rootMargin?: string; // default: '0px 0px 0px 0px' } -const LazyImage: React.ForwardRefExoticComponent & React.RefAttributes> +const LazyImage: React.ForwardRefExoticComponent & React.RefAttributes> ``` ## Usage diff --git a/docs/docs/react/hooks/useIntersectionObserver.mdx b/docs/docs/react/hooks/useIntersectionObserver.mdx index 26e67ed5a..726022a93 100644 --- a/docs/docs/react/hooks/useIntersectionObserver.mdx +++ b/docs/docs/react/hooks/useIntersectionObserver.mdx @@ -8,21 +8,18 @@ Intersection Observer Option을 설정할 수 있습니다.(하단 `Note` 참고 ## Interface ```tsx -interface UseIntersectionObserverProps { +interface UseIntersectionObserverProps extends IntersectionObserverInit { action: (entry: IntersectionObserverEntry) => void; calledOnce?: boolean; - threshold?: number | number[]; - root?: Document | Element | null; - rootMargin?: string; } -const useIntersectionObserver: ({ - action, - calledOnce, - root, - threshold, - rootMargin -}: UseIntersectionObserverProps) => React.RefObject +const useIntersectionObserver: ({ + action, + calledOnce, + root, + threshold, + rootMargin, +}: UseIntersectionObserverProps) => (node: T) => void; ``` ## Usage diff --git a/packages/react/src/components/LazyImage/index.tsx b/packages/react/src/components/LazyImage/index.tsx index d3fb621ca..41cf326df 100644 --- a/packages/react/src/components/LazyImage/index.tsx +++ b/packages/react/src/components/LazyImage/index.tsx @@ -2,11 +2,10 @@ import React, { CSSProperties, forwardRef, useMemo } from 'react'; import { useIntersectionObserver } from '../../hooks/useIntersectionObserver'; import { useMergeRefs } from '../../hooks/useMergeRefs'; -interface LazyImageProps extends React.ComponentProps<'img'> { +interface LazyImageProps + extends React.ComponentProps<'img'>, + IntersectionObserverInit { src: string; - threshold?: number | number[]; - root?: Document | Element | null; - rootMargin?: string; } export const LazyImage = forwardRef( diff --git a/packages/react/src/hooks/useIntersectionObserver/index.ts b/packages/react/src/hooks/useIntersectionObserver/index.ts index ffa44e12f..c7ad0eb80 100644 --- a/packages/react/src/hooks/useIntersectionObserver/index.ts +++ b/packages/react/src/hooks/useIntersectionObserver/index.ts @@ -1,66 +1,54 @@ -import { useCallback, useEffect, useRef } from 'react'; +import { useRef } from 'react'; import { usePreservedCallback } from '../usePreservedCallback'; +import { noop } from '@modern-kit/utils'; -export interface UseIntersectionObserverProps { +export interface UseIntersectionObserverProps extends IntersectionObserverInit { action: (entry: IntersectionObserverEntry) => void; calledOnce?: boolean; - threshold?: number | number[]; - root?: Document | Element | null; - rootMargin?: string; } export const useIntersectionObserver = ({ action, calledOnce = false, root = null, - threshold = 0, + threshold = [0], rootMargin = '0px 0px 0px 0px', }: UseIntersectionObserverProps) => { - const ref = useRef(null); - const callbackAction = usePreservedCallback(action); + const intersectionObserverRef = useRef(null); - const observerCallback = useCallback( + const callbackAction = usePreservedCallback(action ?? noop); + + const observerAction = usePreservedCallback( ([entry]: IntersectionObserverEntry[], observer: IntersectionObserver) => { - if (entry) { - if (entry.isIntersecting) { - const targetElement = entry.target as HTMLElement; + if (entry && entry.isIntersecting) { + const targetElement = entry.target as T; - if (callbackAction) { - callbackAction(entry); - } + if (callbackAction) { + callbackAction(entry); + } - if (calledOnce) { - observer.unobserve(targetElement); - } + if (calledOnce) { + observer.unobserve(targetElement); } } - }, - [callbackAction, calledOnce] - ); - - useEffect(() => { - const targetElement = ref.current; - - if (typeof IntersectionObserver === 'undefined') { - return; } + ); - if (!targetElement) { - return; + const targetRef = usePreservedCallback((node: T) => { + if (intersectionObserverRef.current) { + intersectionObserverRef.current.disconnect(); } - const observer = new IntersectionObserver(observerCallback, { + intersectionObserverRef.current = new IntersectionObserver(observerAction, { root, - rootMargin, threshold, + rootMargin, }); - observer.observe(targetElement); - - return () => { - observer.unobserve(targetElement); - }; - }, [root, threshold, rootMargin, observerCallback]); + if (node) { + intersectionObserverRef.current.observe(node); + } + }); - return ref; + return targetRef; };