diff --git a/src/components/OceanSwiper/OceanSwiper.context.ts b/src/components/OceanSwiper/OceanSwiper.context.ts index 0aac068..fd75d25 100644 --- a/src/components/OceanSwiper/OceanSwiper.context.ts +++ b/src/components/OceanSwiper/OceanSwiper.context.ts @@ -1,18 +1,19 @@ import { createStaticContext } from '@/utils/context/StaticContext'; -export interface ContextDispatchOceanSwiperProps {} - interface ContextOceanSwiper { + swiperWrapperRef: { + current: HTMLDivElement | null; + }; buttonRefs: { prev: HTMLButtonElement | null; next: HTMLButtonElement | null; }; } -export const ContextDispatchOceanSwiper = - createStaticContext({}); - export const ContextValueOceanSwiper = createStaticContext({ + swiperWrapperRef: { + current: null, + }, buttonRefs: { prev: null, next: null, diff --git a/src/components/OceanSwiper/OceanSwiper.tsx b/src/components/OceanSwiper/OceanSwiper.tsx index 643aeb0..d10febf 100644 --- a/src/components/OceanSwiper/OceanSwiper.tsx +++ b/src/components/OceanSwiper/OceanSwiper.tsx @@ -1,36 +1,48 @@ 'use client'; -import React, { PropsWithChildren, useEffect, useRef, useState } from 'react'; +import React, { PropsWithChildren, useEffect, useRef } from 'react'; import { A11y } from 'swiper/modules'; import { Swiper, SwiperRef, SwiperSlide, useSwiper } from 'swiper/react'; import { SwiperOptions } from 'swiper/types'; import Tag from '@/composable/Tag/Tag'; import { customEvents } from '@/const/customEvents'; import { getStaticContext } from '@/utils/context/StaticContext'; -import { - ContextDispatchOceanSwiper, - ContextValueOceanSwiper, -} from './OceanSwiper.context'; - -// TODO : 이벤트 위임 구현과 UI 컨트롤 로직을 위한 컨텍스트 API 사용 +import { ContextValueOceanSwiper } from './OceanSwiper.context'; const OceanSwiper = ({ children }: PropsWithChildren) => { return ( - - -
{children}
-
-
+ + {children} + ); }; +const Wrap = ({ children }: PropsWithChildren) => { + const { swiperWrapperRef } = getStaticContext(ContextValueOceanSwiper); + const divRef = useRef(null); + + useEffect(() => { + swiperWrapperRef.current = divRef.current; + typeof window !== 'undefined' && + window.dispatchEvent( + new CustomEvent(customEvents.SWIPER_INIT, { + detail: swiperWrapperRef.current, + }), + ); + }, []); + return
{children}
; +}; + interface MainProps extends SwiperOptions { hiddenNavigation?: boolean; prevButton?: React.ReactNode; @@ -44,6 +56,7 @@ const Main = ({ nextButton, ...rest }: PropsWithChildren) => { + const { swiperWrapperRef } = getStaticContext(ContextValueOceanSwiper); const swiperRef = useRef(null); return ( @@ -53,8 +66,8 @@ const Main = ({ modules={[A11y]} slidesPerView={1} onRealIndexChange={(e) => { - typeof window !== 'undefined' && - window.dispatchEvent( + swiperWrapperRef.current && + swiperWrapperRef.current.dispatchEvent( new CustomEvent(customEvents.SWIPER_REAL_INDEX_CHANGE, { detail: e, }), @@ -116,6 +129,7 @@ const Button = ({ className, style, direction, hidden }: ButtonProps) => { ); }; +OceanSwiper.Wrap = Wrap; OceanSwiper.Top = Tag; OceanSwiper.Main = Main; OceanSwiper.Slide = SwiperSlide; diff --git a/src/const/customEvents.ts b/src/const/customEvents.ts index 8e74631..03eee19 100644 --- a/src/const/customEvents.ts +++ b/src/const/customEvents.ts @@ -1,3 +1,4 @@ export const customEvents = { SWIPER_REAL_INDEX_CHANGE: 'SWIPER_REAL_INDEX_CHANGE', + SWIPER_INIT: 'SWIPER_INIT', } as const; diff --git a/src/hook/useEventListener.ts b/src/hook/useEventListener.ts index 5527a59..a5e5e76 100644 --- a/src/hook/useEventListener.ts +++ b/src/hook/useEventListener.ts @@ -1,7 +1,7 @@ import { useEffect } from 'react'; const useEventListener = ( - target: EventTarget | undefined, + target: EventTarget | undefined | null, event: string, callback: (event: T) => void, options?: boolean | AddEventListenerOptions, @@ -11,13 +11,13 @@ const useEventListener = ( }; useEffect(() => { - typeof target !== 'undefined' && - target.addEventListener(event, eventListener, options); + if (!target) return; + + target.addEventListener(event, eventListener, options); return () => { - typeof target !== 'undefined' && - target.removeEventListener(event, eventListener, options); + target.removeEventListener(event, eventListener, options); }; - }, []); + }, [target]); }; export default useEventListener; diff --git a/src/hook/useOceanSwiperIndex.ts b/src/hook/useOceanSwiperIndex.ts index c1947ea..185f95a 100644 --- a/src/hook/useOceanSwiperIndex.ts +++ b/src/hook/useOceanSwiperIndex.ts @@ -1,18 +1,36 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import Swiper from 'swiper'; +import { ContextValueOceanSwiper } from '@/components/OceanSwiper/OceanSwiper.context'; import { customEvents } from '@/const/customEvents'; +import { getStaticContext } from '@/utils/context/StaticContext'; +import { eventManager } from '@/utils/eventManager'; import useEventListener from './useEventListener'; export const useOceanSwiperIndex = () => { + const { swiperWrapperRef } = getStaticContext(ContextValueOceanSwiper); const [readIndex, setIndex] = useState(0); - useEventListener>( - typeof window !== 'undefined' ? window : undefined, + const { add, remove } = eventManager( + swiperWrapperRef, customEvents.SWIPER_REAL_INDEX_CHANGE, (e) => { - setIndex(e.detail.realIndex); + setIndex((e as CustomEvent).detail.realIndex); }, ); + useEventListener>( + typeof window !== 'undefined' ? window : null, + customEvents.SWIPER_INIT, + (e) => { + if (e.detail === swiperWrapperRef.current) add(); + }, + ); + + useEffect(() => { + return () => { + remove(); + }; + }, []); + return { readIndex }; }; diff --git a/src/utils/eventManager.ts b/src/utils/eventManager.ts new file mode 100644 index 0000000..ef57b2e --- /dev/null +++ b/src/utils/eventManager.ts @@ -0,0 +1,27 @@ +export const eventManager = ( + target: { + current: EventTarget | null; + }, + event: string, + callback: (event: T) => void, + options?: boolean | AddEventListenerOptions, +) => { + const eventListener: EventListener = (event: Event) => { + return callback(event as T); + }; + + const add = () => { + if (target.current) + target.current.addEventListener(event, eventListener, options); + }; + + const remove = () => { + if (target.current) + target.current.removeEventListener(event, eventListener, options); + }; + + return { + add, + remove, + }; +};