Language/React

Swiper - Cannot read properties of undefined (reading 'autoplay')

건담아빠 2024. 11. 7. 11:47

React에서 Swiper를 추가하고 autoplay를 시키고 해당 화면에서 나오게 되면 아래와 같은 에러가 발생한다.

Unhandled Runtime Error

TypeError: Cannot read properties of undefined (reading 'autoplay')

 

 

원인

GPT 형님께서 말씀하시길 `TypeError: Cannot read properties of undefined (reading 'stop') 오류가 발생하는 경우는 대개 stop 메서드를 호출하려는 객체가 정의되지 않았거나 아직 초기화되지 않았기 때문입니다. 이 오류는 swiperInstance.autoplay.stop() 호출에서 자주 발생할 수 있는데, autoplay 객체가 아직 인스턴스에 초기화되지 않았거나, 다른 이유로 인해 undefined인 상태일 때 그렇습니다.` 라고 하셨고 테스트 진행해보니 그문제가 맞았다.

 

방법1

컴포넌트가 사라질때 stop 및 destroy하는 코드를 추가하였다.

  useEffect(() => {
    // 컴포넌트 언마운트 시 Swiper 인스턴스 정리
    return () => {
      if (swiperInstance) {
        if (swiperInstance.autoplay && swiperInstance.autoplay.stop) {
          swiperInstance.autoplay.stop();
        }
        swiperInstance.destroy(true, true);
      }
    };
  }, [swiperInstance]); // swiperInstance가 변경될 때마다 이 effect를 다시 실행

 

import React, { useEffect, useState } from 'react';

...

SwiperCore.use([Autoplay]);

const RollingNotice = () => {
  const [swiperInstance, setSwiperInstance] = useState<SwiperCore | null>(null);

  useEffect(() => {
    // 컴포넌트 언마운트 시 Swiper 인스턴스 정리
    return () => {
      if (swiperInstance) {
        if (swiperInstance.autoplay && swiperInstance.autoplay.stop) {
          swiperInstance.autoplay.stop();
        }
        swiperInstance.destroy(true, true);
      }
    };
  }, [swiperInstance]); // swiperInstance가 변경될 때마다 이 effect를 다시 실행

  ...

  return (
    <div className={cx('wrap-rolling-notices')}>
      <Swiper
        onSwiper={setSwiperInstance} // Swiper 인스턴스를 상태에 저장
        modules={[Autoplay]}
        ...
      >
        {items.map((item, index) => (
          <SwiperSlide ...>
            ...
          </SwiperSlide>
        ))}
      </Swiper>
    </div>
  );
};

export default RollingNotice;

 

방법2

Swiper에서 onBeforeDestroy을 활용하는것이다.

  <Swiper
    onBeforeDestroy={(swiper) => {
      // Ensure autoplay is stopped before component unmounts
      if (swiper.autoplay) {
        swiper.autoplay.stop();
      }
    }}
    ...
  >

 

<Swiper
  onBeforeDestroy={(swiper) => {
    // Ensure autoplay is stopped before component unmounts
    if (swiper.autoplay) {
      swiper.autoplay.stop();
    }
  }}
  
  ...
>
  {items &&
    items.map((item, idx) => (
      <SwiperSlide ...>
        ...
      </SwiperSlide>
    ))}
</Swiper>

 

 

입맛에 맞게 쓰면 되겠지만 `방법1`은 리액트에서 컴포턴트가 사라질때 컨트롤 하는것이고,  `방법2`는 Swiper 컴포넌트에서 제공되는 함수를 활용 하는것이다.

 

필자는 `방법2`가 소스 가독성으로도 좋아서 2번 방법으로 적용하였다.

 

 

추가적인 오류

swiper.params.autoplay.reverseDirection 이부분에서 에러 발생

reverseDirection: false 옵션 추가

<Swiper
    ...
    autoplay={{
      delay: 3000,
      disableOnInteraction: false,
      reverseDirection: false
    }}
    ...
>
  ...
</Swiper>