リアクトでスワイパー要素を使用

2023年3月7日

2023年7月28日に更新 - 仮想スライドの実装追加

リアクトでスワイパーウェブコンポーネントを使用する方法の詳細をご紹介します。

パラメーター

リアクトはカスタム要素(ウェブコンポーネント)をネイティブではまだサポートしていないため、このコードは

<swiper-container
  slides-per-view={3}
  breakpoints={{
    768: {
      slidesPerView: 4,
    },
  }}
>
  ...
</swiper-container>

実際には以下のようにレンダリングされます。

<swiper-container slides-per-view="3" breakpoints="[object Object]">
  ...
</swiper-container>

これは期待したものと異なります。

このため、プロパティとしてパラメーターを渡す、カスタム初期化を使用する必要がスワイパー要素をリアクトで使用することになります

import { useEffect, useRef } from 'react';
import { register } from 'swiper/element/bundle';

export default function App() {
  const swiperRef = useRef(null);

  useEffect(() => {
    // Register Swiper web component
    register();

    // Object with parameters
    const params = {
      slidesPerView: 3,
      breakpoints: {
        768: {
          slidesPerView: 4,
        },
      },
    };

    // Assign it to swiper element
    Object.assign(swiperRef.current, params);

    // initialize swiper
    swiperRef.current.initialize();
  }, []);

  return (
    <swiper-container init="false" ref={swiperRef}>
      ...
    </swiper-container>
  );
}

イベント

イベントも同様に、リアクトのon[Event]構文を使用して割り当てられず、.addEventListenerメソッドを使用するか、パラメーターでonに渡すことで追加する必要があります

import { useEffect, useRef } from 'react';
import { register } from 'swiper/element/bundle';

export default function App() {
  const swiperRef = useRef(null);

  useEffect(() => {
    // Register Swiper web component
    register();

    // Add event listener
    swiperRef.current.addEventListener('swiperslidechange', (e) => {
      console.log(e.detail);
    });

    // Object with parameters
    const params = {
      // or pass it in on
      on: {
        slideChange(s) {
          console.log(s);
        },
      },
    };

    // Assign it to swiper element
    Object.assign(swiperRef.current, params);

    // initialize swiper
    swiperRef.current.initialize();
  }, []);

  return (
    <swiper-container init="false" ref={swiperRef}>
      ...
    </swiper-container>
  );
}

カスタムラッパコンポーネント

プロジェクトにスワイパーがたくさんある場合は、スワイパー要素をラップするシンプルなリアクトコンポーネントを作成できます

import { useEffect, useRef } from 'react';
import { register } from 'swiper/element/bundle';

export function Swiper(props) {
  const swiperRef = useRef(null);
  const {
    children,
    ...rest
  } = props;

  useEffect(() => {
    // Register Swiper web component
    register();

    // pass component props to parameters
    const params = {
      ...rest
    };

    // Assign it to swiper element
    Object.assign(swiperRef.current, params);

    // initialize swiper
    swiperRef.current.initialize();
  }, []);

  return (
    <swiper-container init="false" ref={swiperRef}>
      {children}
    </swiper-container>
  );
}
export function SwiperSlide(props) {
  const {
    children,
    ...rest
  } = props;

  return (
    <swiper-slide {...rest}>
      {children}
    </swiper-slide>
  );
}

これで、アプリ全体で次のように使用できます。

import { Swiper, SwiperSlide } from 'path/to/my-swiper.jsx';

export default function App() {
  return (
    <Swiper
      slidesPerView={3}
      breakpoints={{ 768: { slidesPerView: 4 } }}
      on={{
        slideChange: () => console.log('slide changed'),
        progress: (s, progress) => console.log(`progress is ${progress}`),
      }}
    >
      <SwiperSlide>Slide 1</SwiperSlide>
      <SwiperSlide>Slide 2</SwiperSlide>
      <SwiperSlide>Slide 3</SwiperSlide>
    </Swiper>
  );
}

仮想スライド

仮想スライドを使用する場合、さらに掘り下げて仮想スライドをリアクトでも実装できます。次の方法でSwiperコンポーネントを少し変更する必要があります。

import React, { useEffect, useState, useRef } from 'react';
import { register } from 'swiper/element/bundle';

export function Swiper(props) {
  const swiperRef = useRef(null);
  const {
    children,
    virtual, // specify virtual prop
    ...rest
  } = props;

  // store virtual data
  const [virtualData, setVirtualData] = useState({
    from: 0,
    to: 0,
    offset: 0,
    slides: [],
  });

  useEffect(() => {
    // Register Swiper web component
    register();

    // pass component props to parameters
    const params = {
      ...rest
    };

    // if we have virtual prop passed add virtual prameters
    if (virtual) {
      params.virtual = {
        enabled: true,
        // pass component children (slides) in `slides` array
        slides: children,
        // set virtual data on renderExternal
        renderExternal(vd) {
          setVirtualData(vd);
        },
      };
    }

    // Assign it to swiper element
    Object.assign(swiperRef.current, params);

    // initialize swiper
    swiperRef.current.initialize();
  }, []);

  // calc slides to render
  const slides = virtual
    ? virtualData.slides.map((slide, index) =>
        // clone slide
        React.cloneElement(slide, {
          // add key
          key: virtualData.from + index,
          // set slides offset
          style: {
            [props.direction === "vertical" ? "top" : "left"]:
              virtualData.offset,
          },
          // add and swiper slide index to data attribute
          ["data-swiper-slide-index"]: virtualData.from + index,
        })
      )
    : children; // return children if virtual is disabled

  return (
    <swiper-container init="false" ref={swiperRef}>
      {slides}
    </swiper-container>
  );
}

// SwiperSlide component stays the same
export function SwiperSlide(props) {
  const {
    children,
    ...rest
  } = props;

  return (
    <swiper-slide {...rest}>
      {children}
    </swiper-slide>
  );
}

これで、リアクトで仮想スライドを含むスワイパーを簡単に作成できます。

import { Swiper, SwiperSlide } from 'path/to/my-swiper.jsx';

export default function App() {
  return (
    <Swiper virtual navigation>
      <SwiperSlide>Slide 1</SwiperSlide>
      <SwiperSlide>Slide 2</SwiperSlide>
      <SwiperSlide>Slide 3</SwiperSlide>
      ...
      <SwiperSlide>Slide 1000</SwiperSlide>
    </Swiper>
  );
}

追伸

いつも通り、スワイパーがお気に入りの場合は、寄付またはプレッジでプロジェクトをサポートしてください

プレミアムプロジェクトの確認

サポートは私たちにとって非常に重要です!