ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • react-slick 캐러셀 Prev, Next Arrow 버튼 더블 클릭 버그
    Studying/React 2023. 8. 20. 21:48

    문제 발생

    PortfolioItem.tsx 컴포넌트의 캐러셀에서 Prev/Next 버튼에 더블 클릭이 적용되지 않는 문제가 발생했습니다. 여러번 빠르게 클릭한 만큼 캐러셀이 빠르게 넘어가지 않습니다.

    사용한 캐러셀 라이브러리는 react-slick 입니다.

    캐러셀 맨 처음 페이지, 맨 끝 페이지에 도달했을 때 각각 Prev, Next 버튼이 보이지 않도록 만들기 위해 캐러셀의 인덱스를 파악해야 했고, 이를 useState로 관리했습니다.

     

    const [currentSlideIndex, setCurrentSlideIndex] = useState(0);

     

    Prev 버튼 클릭 시 prev - 1 Next 버튼 클릭 시 prev + 1 되어 페이지가 증가/감소합니다.

    버튼을 빠르게 클릭한 만큼 currentSlideIndex는 증감하는데, 캐러셀은 덜 넘어가고, 현재 캐러셀 인덱스와 currentSlideIndex가 불일치하는 문제가 발생합니다.

     

     

    정상 화면이라면 다음과 같아야합니다.

     

    왼쪽부터 1페이지, 2페이지, 3페이지

     

     

    하지만 현재 문제 화면은 다음과 같습니다.

     

    2페이지인데 1페이지로 돌아가는 prev 버튼이 없음

     

    3 페이지에서 Prev 버튼을 빠르게 2번 클릭하면 index -1 연산이 두 번 수행되어 currentSlideIndex는 0이 됩니다. 따라서 Prev 버튼이 사라집니다.

    하지만 캐러셀은 한 페이지만 넘어가느라 아직 2 페이지고, 1페이지로 갈 수 없게됩니다.

     

     

    전체 코드는 다음과 같습니다.

     

    // PortfolioItem.tsx
    
    // 현재 인덱스
    const [currentSlideIndex, setCurrentSlideIndex] = useState(0);
    
    // PrevArrow 컴포넌트 onClick 함수
    const handlePrevious = (button:HTMLButtonElement) => {
      setCurrentSlideIndex((prev) => prev - 1); // 인덱스 1 감소
      slick?.slickPrev(); // 이전 페이지로 이동
    }
    
    // NextArrow 컴포넌트 onClick 함수
    const handleNext = (button:HTMLButtonElement) => {
      setCurrentSlideIndex((prev) => prev + 1); // 인덱스 1 증가
      slick?.slickNext(); // 다음 페이지로 이동
    }

     

     

     

    원인

    react-slick은 캐러셀이 넘어가는 속도인 speed 값이 기본값 500ms으로 제공되기 때문에 발생한 문제였습니다.
    따라서 speed: 0 으로 설정하면 클릭 속도에 맞춰 캐러셀이 넘어갑니다.

    하지만 부드러운 움직임을 유지하고 싶을 경우, 캐러셀이 speed 시간 동안 다 움직이고 정지한 다음에 Prev/Next 버튼이 활성화돼야 합니다.

     

     

     

    해결 방법

    1. lodash의 throttle을 사용하기

    throttle을 사용하여 캐러셀 speed 시간 당 한 번 이벤트가 발생하도록 조정할 수 있습니다.

    하지만 현재 코드의 경우, handlePrev()/handleNext() 함수 안에 <Slider>를 참조하는 slick이 존재합니다.

    throttle을 쓸 경우 리렌더링 되어도 (과거에 계속 호출된) 자기 자신을 기억하기 위해 useCallback을 쓰고 초기화 옵션으로 [](초기 렌더링 때 한 번 초기화)를 지정해야 합니다.

    하지만 throttle 함수가 정의되는 첫 순간에는 아직 DOM이 형성되지 않아 slick이 존재하지 않습니다. 따라서 결국 캐러셀을 넘기지 못하게 되는 겁니다.

     

     

    2. 직접적으로 핸들러 막기

    결국 무식한 방법으로 문제를 해결했습니다.

     

    const [currentSlideIndex, setCurrentSlideIndex] = useState(0); // 현제 캐러셀 인덱스
    const [beforeClicked, setBeforeClicked] = useState(false); // 직전에 버튼이 클릭되었는지 여부
    const [slick, setSlick] = useState<Slider>(); // 캐러셀 컴포넌트 <Slider>
    
    const handlePrev = ()=> {
      if(beforeClicked) return; // 바로 직전에 버튼 클릭했으면 핸들러 작동 x early return
      setBeforeClicked((prev)=>!prev); // 그게 아니면 버튼이 정상적으로 눌린 게 되고 true로 바꿈
      slick?.slickPrev(); // 이전 페이지로 이동
      setCurrentSlideIndex((prev) => prev - 1); // 인덱스 1 감소
      setTimeout(()=>setBeforeClicked((prev)=>!prev), 200); // 캐러셀이 넘어가는 시간(speed)인 200ms가 지나면 다시 버튼 활성화되도록 false로 바꿔줌
    };
    
    const handleNext = ()=> {
      if(beforeClicked) return;
      setBeforeClicked((prev)=>!prev);
      slick?.slickNext();
      setCurrentSlideIndex((prev) => prev + 1);
      setTimeout(()=>setBeforeClicked((prev)=>!prev), 200);
    };

     

    결과는 딱 원하는대로 나왔습니다.

    Next 버튼을 더블 클릭했을 때, 첫 번째 클릭한 순간 캐러셀 이동, 인덱스 +1 증가. 두 번째 클릭은 반영되지 않습니다.

    단점은 해석이 불편한 코드가 되었다는 점 같습니다

     

     

    3. 클릭 횟수만큼 빠르게 넘기기가 가능한 캐러셀 라이브러리를 설치하자.

    제 기억에 부트스트랩도 그렇고 다양한 좋은 캐러셀 라이브러리가 많았습니다.

    하필 react-slick을 처음 써보려고 할 때 이런 문제같지 않은 문제가 발생하네요.

     

    당장 가진 환경에서 문제를 해결하고싶다는 오기로 인해 시도하진 않았지만 가장 현명한 방법 같습니다.

     

     

     

    참고 사이트

     

    중복 submit방지 ( Lodash throttle & debounce)

    폼 제출시 연속으로 클릭하면 여러번 호출되어 중복으로 등록되는 경우가생겨 Lodash 라이브러리를 사용하기로 했다 Lodash 모듈화, 성능 및 기타 기능을 제공하는 자바스크립트 유틸리티 라이브

    dntldh10.tistory.com

Designed by Tistory.