react-slick ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํ์ด์ง์ด๋ ์ค๋ฅํด๊ฒฐ (Slider Syncing(AsNavFor))
๐ฉ ์ค๋ฅ ๋ฐ์ ์ํฉ
1. react-slick ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ Slider Syncing(AsNavFor)์ ์ฌ์ฉ์ค
(https://react-slick.neostack.com/docs/example/as-nav-for)
2. MainCarousel์ item์ ํด๋ฆญํ๋ฉด ํด๋น ์ ๋ ํ์ด์ง๋ก ์ด๋ํ์ง์๋ ์ค๋ฅ ๋ฐ์ (๋ง์ง๋ง ์ ๋์ ํ์ด์ง๋ก ์ด๋)
3. Slider Syncing์์ onClick์ด๋ฒคํธ ๋ฐ์ ์, animeId๊ฐ ์ ๋๋ก ์ ๋ฌ๋์ง ์๋๊ฒ์ผ๋ก ํ์
const handleClick = (e: React.MouseEvent, animeId: number) => {
if (dragging) {
e.stopPropagation();
return;
}
console.log(animeId);
// navigate(`/animes/${animeId}`);
};
return (
<>
{isLoading && <AnimeRankingLoading />}
{animes && (
<>
<AnimeRankingContainer>
<h1>{title}</h1>
<Slider
{...SyncingMainCarousel}
ref={(mainNav) => setMainNav(mainNav ?? undefined)}
asNavFor={subNav}
beforeChange={() => setDragging(true)}
afterChange={() => setDragging(false)}
>
{animes.map((ani, i) => (
<HighlightItemContainer key={ani.animeId}>
<HighlightItem
image={ani.thumbnail}
onClick={(e: React.MouseEvent) =>
handleClick(e, ani.animeId)
}
>
<Rank size="lg">{i + 1}</Rank>
<h3>{ani.genres.join("/")}</h3>
<h2>{ani.title}</h2>
</HighlightItem>
</HighlightItemContainer>
))}
</Slider>
<Slider
{...SyncingSubCarousel}
asNavFor={mainNav}
ref={(subNav) => setSubNav(subNav ?? undefined)}
>
{animes.map((ani, i) => (
<SliderItem key={ani.animeId}>
<SliderItemImage image={ani.thumbnail}>
<Rank>{i + 1}</Rank>
</SliderItemImage>
<div>{ani.title}</div>
</SliderItem>
))}
{/* carousel ๋ ์ด์์ ๋ง๊ฐ์ง ๋ฐฉ์ง: div ์ถ๊ฐ */}
<div />
</Slider>
</AnimeRankingContainer>
</>
)}
</>
);
๐ฉ ํด๊ฒฐ
<HighlightItem> element์ 'anime-id' ๋ฐ์ดํฐ ์์ฑ์ ์ค์ ํ๊ณ , ํด๋น ๊ฐ์ ์ฝ์ด์ค๋ ๋ฐฉ์์ผ๋ก ํด๊ฒฐํ์ต๋๋ค.
1. ๋ Slider๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด main carousel์ ๊ฐ์ธ๋ div ์์ฑ ํ, ref ์ฐ๊ฒฐ
2. anime-id ๋ฐ์ดํฐ ์์ฑ ์ค์
3. main carousel → .slick-current ํด๋์ค → anime-id ๋ฐ์ดํฐ ์์ฑ์ ๊ฐ์ง๊ณ ์๋ element ์ ํ
4. anime-id ๋ฐ์ดํฐ ์์ฑ์ผ๋ก ํด๋น ํ์ด์ง๋ก ์ด๋
export default function AnimeRanking({ title }: AnimeRankingProps) {
const mainCarouselRef = useRef<HTMLDivElement>(null);
const handleClick = (e: React.MouseEvent) => {
// 3. main carousel → .slick-current ํด๋์ค → anime-id ๋ฐ์ดํฐ ์์ฑ์ ๊ฐ์ง๊ณ ์๋ element ์ ํ
const currentElement = mainCarouselRef.current?.querySelector(
".slick-current div[data-anime-id]",
);
if (dragging) {
e.stopPropagation();
return;
}
// 4. anime-id ๋ฐ์ดํฐ ์์ฑ์ผ๋ก ํด๋น ํ์ด์ง๋ก ์ด๋
if (currentElement) {
const el = currentElement as HTMLDivElement;
// console.log(el.dataset.animeId);
navigate(`/animes/${el.dataset.animeId}`);
}
};
return (
<>
{isLoading && <AnimeRankingLoading />}
{animes && (
<>
<AnimeRankingContainer>
<h1>{title}</h1>
{/*
* 1. ๋ Slider๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด main carousel์ ๊ฐ์ธ๋ div ์์ฑ ํ, ref ์ฐ๊ฒฐ
*/}
<div ref={mainCarouselRef}>
<Slider
{...SyncingMainCarousel}
ref={(mainNav) => setMainNav(mainNav ?? undefined)}
asNavFor={subNav}
beforeChange={() => setDragging(true)}
afterChange={() => setDragging(false)}
>
{animes.map((ani, i) => (
<HighlightItemContainer key={ani.animeId}>
<HighlightItem
image={ani.thumbnail}
// 2. anime-id ๋ฐ์ดํฐ ์์ฑ ์ค์
data-anime-id={ani.animeId}
onClick={(e: React.MouseEvent) => handleClick(e)}
>
<Rank size="lg">{i + 1}</Rank>
<h3>{ani.genres.join("/")}</h3>
<h2>{ani.title}</h2>
</HighlightItem>
</HighlightItemContainer>
))}
</Slider>
</div>
<Slider
...์๋ต
</Slider>
</AnimeRankingContainer>
</>
)}
</>
);
}