刷卡器反应 |如何使用 React refs 创建自定义导航/分页组件?



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的滑动,这是字符串选择器会发生的情况。

我目前悲伤和笨拙的解决方法:

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>
)
}

虽然 Pierrat 的答案最初确实为我解决了这个问题,但我遇到了一个错误,即导航按钮在我暂停并重新启动 Swipeper 之前不会做任何事情。

为了修复,我创建了自己的函数来处理更新,并改用了这些函数。

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>
)
}

只是注意onBeforeInit进入胺D样本的小错误。

更正的代码:

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. 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>
)
}

就是这样,所以如果你检查 Swipeper duc,有一个仅用于 API 的页面,您可以在其中找到一个讨论 swipeper 提供的事件的部分,无论如何我希望这很有帮助

重要更新:Swipeper v8.4.4

这个问题的大多数答案都是指 API v6,但例如,更高版本(在撰写此答案时是 v8.4.4)没有swiper.params.navigation.prevEl而是您应该直接从 swipeper 实例访问navigation属性,如下所示:swiper.navigation.prevEl

下面是使用 React v18 + Swipeper 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>
);
};

参考滑动器并从任何您想要的地方控制它!

即使上面的例子有效,有时引用没有得到正确的值,所以不是创建两个用于导航和/或使用setTimeout()分配正确的值的引用,你可以引用滑动器本身并使用slideNext()slidePrev()控制它,参见James Hooper的答案,你可以在提到的答案中省略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>
);
};

打字稿:

下面是上面使用 Typscript 的相同示例:

/* 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>
);
};

在 Swipeper v6.2.0 中显然不可能直接传递引用。

我也为任何最终来到图书馆作者回答的人创建了一个 Github 问题。 https://github.com/nolimits4web/swiper/issues/3855

根据前面的答案,以下答案是完整的代码。这可能有助于您根据需要实施。

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'));

> 2022 解决方案源

这是用于添加自定义paginationnavigation的最新版本解决方案

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>

以这种方式工作:

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>
);

这是最好的解决方案

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;

这段代码来自库创建者的回答,它对我有用。

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>
);

使用 id 而不是类

`navigation={{
prevEl: "#prev_slide",
nextEl: "#next_slide",
}}`

for react/ts

navigation={true}
onNavigationNext={(swiper: any) => (navEvent(swiper, "N"))}
onNavigationPrev={(swiper: any) => (navEvent(swiper, "P"))}

对于那些这些解决方案都不适合您的情况的人(像我一样): 只需使用 React Refs,但请记住,React refs 在第一次渲染时为。 因此,您需要在滑动器初始化期间触发重新渲染,并且所有操作都将按预期工作:

//Add a state that will trigger a re-render later
const [_, setInit] = useState(false)
const prevRef = useRef(null);
const nextRef = useRef(null);
return (
<Swiper
//assign the refs to the swiper navigation buttons
navigation={{
prevEl: prevRef.current,
nextEl: nextRef.current,
}}
//trigger a re-render by updating the state on swiper initialization
onInit={() => setInit(true)}    >
<SwiperSlide>Slide 1</SwiperSlide>
<SwiperSlide>Slide 2</SwiperSlide>
<div ref={prevRef}>Prev</div>
<div ref={nextRef}>Next</div>
</Swiper>
);

我们在 Vue 中遇到了类似的问题,此代码不起作用:

<swiper-container
navigation-prev-el=".button-prev"
navigation-next-el=".button-next"
>
<!-- content -->
</swiper-container>
<button class="button-prev" />
<button class="button-next" />

它不起作用,因为当渲染<swiper-container>时,按钮仍未渲染,因为它们稍后在 DOM 中。

解决方案很简单:将按钮移到 DOM 的上方,以便在渲染<swiper-container>时它们存在:

<button class="button-prev" />
<button class="button-next" />
<swiper-container
navigation-prev-el=".button-prev"
navigation-next-el=".button-next"
>
<!-- content -->
</swiper-container>

最新更新