I'm using Ionic 7, React 18, and Swiper 10.3.1. I have the following component that toggles between two images. I would like to animate it to have a horizontal flip, when someone clicks a button, but I'm having trouble getting a reference to the Swiper in my onClick button handler. I have this

  const swiperRef = useRef(null);
        ...
  const handleSeeNext = () => {
    if (swiperRef.current) {
      swiperRef.current.getSwiper().slideNext();
    }
  };

but I'm told by the compiler

Property 'getSwiper' does not exist on type 'never'.ts(2339)

This is the complete component

import { IonImg } from "@ionic/react";
import React, { useRef } from "react";
import { Swiper, SwiperSlide } from "swiper/react";
//import "swiper/css/swiper.css";
import Card from "../types/card.type";
interface CardComponentProps {
  card: Card;
}

const CardComponent: React.FC<CardComponentProps> = ({ card }) => {
  const swiperRef = useRef(null);
  
  const slideOptions = {
    on: {
      beforeInit(swiper) {
        swiper.params.slidesPerView = 1;
        swiper.params.slidesPerColumn = 1;
        swiper.params.slidesPerGroup = 1;
        swiper.params.watchSlidesProgress = true;
        swiper.params.spaceBetween = 0;
        swiper.params.virtualTranslate = true;

        swiper.classNames.push(`${swiper.params.containerModifierClass}flip`);
        swiper.classNames.push(`${swiper.params.containerModifierClass}3d`);
      },
      setTranslate(swiper) {
        const { slides, rtlTranslate: rtl } = swiper;
        slides.forEach((slide) => {
          const $slideEl = swiper.$(slide);
          let progress = slide.progress;

          if (swiper.params.flipEffect.limitRotation) {
            progress = Math.max(Math.min(slide.progress, 1), -1);
          }

          const offset$$1 = slide.swiperSlideOffset;
          const rotate = -180 * progress;
          let rotateY = rotate;
          let rotateX = 0;
          let tx = -offset$$1;
          let ty = 0;

          if (!swiper.isHorizontal()) {
            ty = tx;
            tx = 0;
            rotateX = -rotateY;
            rotateY = 0;
          } else if (rtl) {
            rotateY = -rotateY;
          }

          $slideEl[0].style.zIndex = -Math.abs(Math.round(progress)) + slides.length;

          $slideEl.transform(`translate3d(${tx}px, ${ty}px, 0px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`);
        });
      },
      setTransition(swiper, duration) {
        const { slides, activeIndex, $wrapperEl } = swiper;

        slides
          .transition(duration)
          .find('.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left')
          .transition(duration);

        if (swiper.params.virtualTranslate && duration !== 0) {
          let eventTriggered = false;

          slides.eq(activeIndex).transitionEnd(() => {
            if (eventTriggered) return;

            eventTriggered = true;
            swiper.animating = false;
            const triggerEvents = ["webkitTransitionEnd", "transitionend"];

            triggerEvents.forEach((eventName) => {
              $wrapperEl.trigger(eventName);
            });
          });
        }
      },
    },
  };

  const handleSeeNext = () => {
    if (swiperRef.current) {
      swiperRef.current.getSwiper().slideNext();
    }
  };

  const frontImgDataUri = `data:${card.frontImgMimeType};base64,${
    card.frontImgBase64Encoded
  }`;
  const backImgDataUri = `data:${card.backImgMimeType};base64,${
    card.backImgBase64Encoded
  }`;

  return (
    <>
    <Swiper {...slideOptions}>
      {/* Your slides go here */}
      <div className="swiper-slide"><IonImg src={frontImgDataUri} alt="Front" /></div>
      <div className="swiper-slide"><IonImg src={backImgDataUri} alt="Back" /></div>
      {/* Add more slides as needed */}
    </Swiper>
    <button onClick={handleSeeNext}>See Next</button>
    </>
  );
};

export default CardComponent;
2

There are 2 best solutions below

1
StackoverBlows On

First, you need to define a state for your Swiper instance:

const [my_swiper, set_my_swiper] = useState<SwiperCore>({});

Then, in your Swiper component, you can use the onInit prop to set your Swiper instance when it initializes:

<Swiper slidesPerView={1} onInit={(swiper) => { set_my_swiper(swiper)}}>
  <SwiperSlide>Slide 1</SwiperSlide>
  <SwiperSlide>Slide 2</SwiperSlide>
</Swiper>

Now, you can use your Swiper instance to call the slideNext() and slidePrev() methods:

my_swiper.slideNext();
my_swiper.slidePrev();

You can call these methods anywhere to dynamically control your Swiper.

If you want to navigate to a specific slide, you can use the slideTo() method:

my_swiper.slideTo(number);

For the button click, you can use the onClick prop of your button and call the slideNext() or slidePrev() methods inside it:

<IonButton onClick={() => my_swiper.slideNext()}>Next Slide</IonButton>
0
hygtfrde On

It's possible that swiperRef.current has not been mounted yet or it's of the wrong type. Try mounting the swiperRef in the parent component first. If that doesn't fix it, you could try explicit type casting swiperRef.current as Swiper.

const swiperRef = useRef<Swiper>(null);
const handleSeeNext = () => {
  if (swiperRef.current) {
    (swiperRef.current as Swiper).slideNext();
  }
};