Swiper React | 如何使用React引用创建自定义导航/分页组件?

37

SwiperJS 文档 显示 导航 prevEl/nextEl 可以是 "string" 或 "HTMLElement" 类型。使用字符串选择器很容易,如:

const MySwiper = (props) => (
  <Swiper
    navigation={{
      prevEl: '.prev',
      nextEl: '.next',
    }}
    {...props}
  >
    <SwiperSlide>slide 1</SwiperSlide>
    <SwiperSlide>slide 2</SwiperSlide>
    <div className="prev" />
    <div className="next" />
  </Swiper>
)

然而,如何在React引用中正确实现这一点?使用HTML节点而不是字符串选择器使得导航prevEl/nextEl仅限于每个渲染的MySwiper实例。

const App = () => (
  <div>
    <MySwiper className="mySwiper1" />
    <MySwiper className="mySwiper2" />
  </div>
)
在上面的应用程序示例中,从.mySwiper2导航prevEl/nextEl不应该触发.mySwiper1的滑动,这是使用字符串选择器会发生的情况。
我当前的悲伤和hacky解决方法:
const MySwiper = () => {
  const navigationPrevRef = React.useRef(null)
  const navigationNextRef = React.useRef(null)

  return (
    <Swiper
      navigation={{
        // Both prevEl & nextEl are null at render so this does not work
        prevEl: navigationPrevRef.current,
        nextEl: navigationNextRef.current,
      }}
      onSwiper={(swiper) => {
        // Delay execution for the refs to be defined
        setTimeout(() => {
          // Override prevEl & nextEl now that refs are defined
          swiper.params.navigation.prevEl = navigationPrevRef.current
          swiper.params.navigation.nextEl = navigationNextRef.current

          // Re-init navigation
          swiper.navigation.destroy()
          swiper.navigation.init()
          swiper.navigation.update()
        })
      }}
    >
      <SwiperSlide>slide 1</SwiperSlide>
      <SwiperSlide>slide 2</SwiperSlide>
      <div ref={navigationPrevRef} />
      <div ref={navigationNextRef} />
    </Swiper>
  )
}
14个回答

27
虽然 Pierrat的答案 初步解决了我的问题,但我遇到了一个错误,即导航按钮在我暂停并重新启动Swiper之前不起作用。 为了解决这个问题,我创建了自己的处理更新的函数,并使用它们代替原来的函数。
const MyComponent = () => {
  const sliderRef = useRef(null);

  const handlePrev = useCallback(() => {
    if (!sliderRef.current) return;
    sliderRef.current.swiper.slidePrev();
  }, []);

  const handleNext = useCallback(() => {
    if (!sliderRef.current) return;
    sliderRef.current.swiper.slideNext();
  }, []);

  return (
    <div>
      <Swiper ref={sliderRef}>
        <SwiperSlide />
        ...slides
        <SwiperSlide />
      </Swiper>
      <div className="prev-arrow" onClick={handlePrev} />
      <div className="next-arrow" onClick={handleNext} />
    </div>
  )
}

我遇到了和你一样的 bug,但是在 pierrat.dev 的解决方案下解决了我的问题。谢谢。 - Ziya Karagöz

22

在查看Amine D的示例时,请注意onBeforeInit可能会有一个小错误。

修正后的代码:

const MySwiper = () => {
  const navigationPrevRef = React.useRef(null)
  const navigationNextRef = React.useRef(null)
  return (
    <Swiper
      navigation={{
        prevEl: navigationPrevRef.current,
        nextEl: navigationNextRef.current,
      }}
     onBeforeInit={(swiper) => {
          swiper.params.navigation.prevEl = navigationPrevRef.current;
          swiper.params.navigation.nextEl = navigationNextRef.current;
     }}
    >
      <SwiperSlide>slide 1</SwiperSlide>
      <SwiperSlide>slide 2</SwiperSlide>
      <div ref={navigationPrevRef} />
      <div ref={navigationNextRef} />
    </Swiper>
  )
}

1
TS2339:类型“boolean | NavigationOptions”上不存在属性“prevEl”。类型“false”上不存在属性“prevEl”。 - cordial
1
为了解决 @cordial 的 TS 问题:import {NavigationOptions} from 'swiper/types/modules/navigation' 并使用类型转换:const navigation = swiper.params.navigation as NavigationOptions - Alec

18

重要更新:Swiper v8.4.4

这个问题的大多数回答都是针对API v6的,但更晚版本(例如目前写下这篇答案时的v8.4.4)不再有swiper.params.navigation.prevEl。相反,您应该直接从swiper实例中访问navigation属性,像这样:swiper.navigation.prevEl

以下是使用React v18 + Swiper v8.4.4的更新示例(不建议使用)

import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation } from 'swiper';

import 'swiper/css';

const Carousel = () => {
  const navigationNextRef = useRef(null);
  const navigationPrevRef = useRef(null);

  return (
    <div>
      <Swiper
        modules={[Navigation]}
        navigation={{
          prevEl: navigationPrevRef.current,
          nextEl: navigationNextRef.current,
        }}
        onBeforeInit={(swiper) => {
          swiper.navigation.nextEl = navigationNextRef.current;
          swiper.navigation.prevEl = navigationPrevRef.current;
        }}
      >
        <SwiperSlide>
          Slide 1
        </SwiperSlide>

        <SwiperSlide>
          Slide 2
        </SwiperSlide>
      </Swiper>
      <div>
        <button ref={navigationNextRef}>Next</button>
        <button ref={navigationPrevRef}>Prev</button>
      </div>
    </div>
  );
};

引用swiper并从任何地方控制它!

虽然上面的示例可以工作,但有时引用未获得正确的值,因此,您可以引用swiper本身并使用slideNext()slidePrev()来控制它,而不是为导航创建两个引用和/或使用setTimeout()分配正确的值。请参见詹姆斯·胡珀的答案,您可以像这样省略所提到的答案中的useCallback()

import { useRef } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation } from 'swiper';

import 'swiper/css';

const Carousel = () => {
  const swiperRef = useRef();

  return (
    <div>
      <Swiper
        modules={[Navigation]}
        onBeforeInit={(swiper) => {
          swiperRef.current = swiper;
        }}
      >
        <SwiperSlide>Slide 1</SwiperSlide>
        <SwiperSlide>Slide 2</SwiperSlide>
      </Swiper>
      <div>
        <button onClick={() => swiperRef.current?.slidePrev()}>Prev</button>
        <button onClick={() => swiperRef.current?.slideNext()}>Next</button>
      </div>
    </div>
  );
};

Typescript:

这里是使用TypeScript的相同示例:

/* eslint-disable import/no-unresolved */

// The rule above to shut ESLint from complaining
// about unresolved Swiper's CSS imports
// Why? see: https://github.com/import-js/eslint-plugin-import/issues/2266


import { useRef } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Swiper as SwiperType, Navigation } from 'swiper';

import 'swiper/css';

const Carousel = () => {
  const swiperRef = useRef<SwiperType>();

  return (
    <div>
      <Swiper
        modules={[Navigation]}
        onBeforeInit={(swiper) => {
          swiperRef.current = swiper;
        }}
      >
        <SwiperSlide>Slide 1</SwiperSlide>
        <SwiperSlide>Slide 2</SwiperSlide>
      </Swiper>
      <div>
        <button onClick={() => swiperRef.current?.slidePrev()}>Prev</button>
        <button onClick={() => swiperRef.current?.slideNext()}>Next</button>
      </div>
    </div>
  );
};


谢谢您。我在想是否有一种方法可以像这样在根组件中获取swiper实例。到目前为止,我只能在文档中找到useSwiper() - Guinevere
@Guinevere 你可以在根组件中创建ref并向下传递,或者你也可以使用“Forwarding Refs”。 https://reactjs.org/docs/forwarding-refs.html - Yassine Sedrani
@YassineSedrani 做得好。运行得很顺利! - Hiszpan

17

我想我解决了这个问题,我也遇到了同样的问题,但最终我们可以开始了

 1. import SwiperCore, { Navigation} from 'swiper'
 2. SwiperCore.use([Navigation])
 3. i will use your exmaple:     

    const MySwiper = () => {
      const navigationPrevRef = React.useRef(null)
      const navigationNextRef = React.useRef(null)
      return (
        <Swiper
          navigation={{
            prevEl: navigationPrevRef.current,
            nextEl: navigationNextRef.current,
          }}
         onBeforeInit={{
              swiper.params.navigation.prevEl = navigationPrevRef.current;
              swiper.params.navigation.nextEl = navigationNextRef.current;
         }}
        >
          <SwiperSlide>slide 1</SwiperSlide>
          <SwiperSlide>slide 2</SwiperSlide>
          <div ref={navigationPrevRef} />
          <div ref={navigationNextRef} />
        </Swiper>
      )
    }

就是这样,所以如果您查看Swiper文档,会发现有一个专门介绍API的页面,在那里您可以找到一个关于Swiper提供的事件的部分。无论如何,我希望这很有帮助。


3
这个问题涉及到分页和导航。 - Lafi
感谢onBeforeInit,它帮助了我,因为Swiper显示了上一页和下一页的按钮,但是点击操作无法正常工作。 - ProblemsEverywhere

5

4

根据之前的回答,以下是完整的代码。它可能有助于您按照所需实现。

import React from "react";
import SwiperCore, { Navigation } from 'swiper';
import { Swiper, SwiperSlide } from "swiper/react";

SwiperCore.use([Navigation]);

const MySwiper = () => {
      const navigationPrevRef = React.useRef(null)
      const navigationNextRef = React.useRef(null)
      return (
        <Swiper
          navigation={{
            prevEl: navigationPrevRef.current,
            nextEl: navigationNextRef.current,
          }}
         setTimeout(() => {
          // Override prevEl & nextEl now that refs are defined
          swiper.params.navigation.prevEl = navigationPrevRef.current
          swiper.params.navigation.nextEl = navigationNextRef.current

          // Re-init navigation
          swiper.navigation.destroy()
          swiper.navigation.init()
          swiper.navigation.update()
        })
        >
          <SwiperSlide>slide 1</SwiperSlide>
          <SwiperSlide>slide 2</SwiperSlide>
          <div ref={navigationPrevRef} />
          <div ref={navigationNextRef} />
        </Swiper>
      )
    }

const App = () => (
  <div>
    <MySwiper className="mySwiper1" />
    <MySwiper className="mySwiper2" />
  </div>
)

ReactDOM.render(<App/>, document.getElementById('root'));

2

这段代码来自于库的作者所提供的答案,并且对我有用。

  const prevRef = useRef(null);
  const nextRef = useRef(null);

  return (
    <Swiper
      onInit={(swiper) => {
        swiper.params.navigation.prevEl = prevRef.current;
        swiper.params.navigation.nextEl = nextRef.current;
        swiper.navigation.init();
        swiper.navigation.update();
      }}
    >
      <SwiperSlide>Slide 1</SwiperSlide>
      <SwiperSlide>Slide 2</SwiperSlide>
      <div ref={prevRef}>Prev</div>
      <div ref={nextRef}>Next</div>
    </Swiper>
  );

2

这里是最佳解决方案

 import React, { useRef } from "react";
 // For Typescript 
 // import SwiperCore from "swiper";
 import { Swiper, SwiperSlide } from "swiper/react";
 import "swiper/css";


 const SliderComponent = () => {
 const swiperRef = useRef();

// For Typescript!
// const swiperRef = useRef<SwiperCore>();  


const sliderSettings = {
  440: {
    slidesPerView: 1,
    spaceBetween: 30,
  },
  680: {
    slidesPerView: 2,
    spaceBetween: 30,
  },
  1024: {
    slidesPerView: 3,
    spaceBetween: 30,
  },
};

return (
    <div>
      <button onClick={() => swiperRef.current?.slidePrev()}>Prev</button>

      <Swiper
        slidesPerView={3}
        breakpoints={sliderSettings}
        onBeforeInit={(swiper) => {
          swiperRef.current = swiper;
        }}
      >
        <SwiperSlide>
          Slide 1
        </SwiperSlide>
        <SwiperSlide>
          Slide 2
        </SwiperSlide>
        <SwiperSlide>
          Slide 3
        </SwiperSlide>
        <SwiperSlide>
          Slide 4
        </SwiperSlide>
        <SwiperSlide>
          Slide 5
        </SwiperSlide>
      </Swiper>

      <button onClick={() => swiperRef.current?.slideNext()}>Next</button>
    </div>
  );
};

export default SliderComponent;

2

2022解决方案 源代码

这是最新版本的解决方案,用于添加自定义的分页导航

import { Swiper, SwiperSlide } from 'swiper/react';
import { Pagination } from 'swiper';
import 'swiper/css';
import 'swiper/css/pagination';
import 'swiper/css/navigation';

同时:

<Swiper
    modules={[Pagination]}
    pagination={{
        el: '.custom-pagination',
        clickable: true
    }}
    navigation={{
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
    }}
>

您可以在任何地方以任何样式放置您的元素!

<div className="custom-pagination"></div>

1
也许你想在你的回答中添加<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>元素。谢谢。 - Abdulkadir KG

1

这是工作原理:

  const prevRef = useRef(null);
  const nextRef = useRef(null);
  return (
    <Swiper
      onInit={(swiper) => {
        swiper.params.navigation.prevEl = prevRef.current;
        swiper.params.navigation.nextEl = nextRef.current;
        swiper.navigation.init();
        swiper.navigation.update();
      }}
    >
      <SwiperSlide>Slide 1</SwiperSlide>
      <SwiperSlide>Slide 2</SwiperSlide>
      <div ref={prevRef}>Prev</div>
      <div ref={nextRef}>Next</div>
    </Swiper>
  );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接