React

react-slick ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ํŽ˜์ด์ง€์ด๋™ ์˜ค๋ฅ˜ํ•ด๊ฒฐ (Slider Syncing(AsNavFor))

presentKey 2023. 12. 12. 02:43

๐Ÿšฉ ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์ƒํ™ฉ

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>
        </>
      )}
    </>
  );

 

ํ•ญ์ƒ ๋งˆ์ง€๋ง‰ ์• ๋‹ˆ์˜ id๊ฐ€ ์ „๋‹ฌ๋จ

 

๐Ÿšฉ ํ•ด๊ฒฐ

<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>
        </>
      )}
    </>
  );
}