Prev 和 Next 按钮在断点处无法正常工作

Posted

技术标签:

【中文标题】Prev 和 Next 按钮在断点处无法正常工作【英文标题】:Prev and Next button is not working correctly at the breakpoints 【发布时间】:2020-11-07 04:57:40 【问题描述】:

各位,我在react-slick 滑块中遇到了问题。我在滑块上渲染一张卡片取决于数组长度。我还在渲染自定义 nextprevious 按钮,它们触发 nextprevious 函数来更改幻灯片。我也希望这个滑块能够响应。这意味着它具有根据定义的设置滚动幻灯片的功能。

我为断点定义的设置

992 > slidesToScroll = 3

577 - 991slidesToScroll = 2

0 - 576slidesToScroll = 1

当没有向前滑动时next 按钮将被禁用,当没有向后滑动时previous 按钮将被禁用。为了实现这个功能,有一个名为 afterChange 的函数,它返回幻灯片的当前活动索引。所以我将该索引分配给state。 但我认为我对next 按钮的逻辑不正确。

我有什么问题?

在调整窗口大小时有什么方法可以refresh滑块,因为幻灯片没有根据breakpoints重置?

即使没有向前滑动,next 按钮也不会在断点 (0 - 576) 处被禁用。

当用户更改单张幻灯片然后调整窗口大小时 要移至下一张幻灯片,Next 按钮将变为非活动状态。

我认为问题在于我写在 renderArrows 函数中的逻辑。

看到这个

代码沙盒:View this

代码:

import React,  useState, useRef  from "react";
import Slider from "react-slick";

// Constant Variables
// Slides scroll behavior on different sizes
const TOTAL_SLIDES = 6;
const DESKTOP_SLIDES_SCROLL = 3;
const TABLET_SLIDES_SCROLL = 2;
const MOBILE_SLIDES_SCROLL = 1;

/**
 * It will return the JSX and register the callbacks for next and previous slide.
 * @param prevCallback function - Go back to the previous slide
 * @param nextCallback function - Go to the next slide
 * @param state object - Holds the state of your slider indexes
 * @param totalSlides number - Holds the total number of slides
 * @return * - Returns the JSX
 */
const renderArrows = (
  prevCallback,
  nextCallback,
   currentIndex, slidesToScroll ,
  totalSlides
) => 
  const cycleDots =
    currentIndex === 0 ? 1 : Math.ceil(totalSlides / slidesToScroll);
  return (
    <div>
      <button disabled=currentIndex === 0 onClick=prevCallback>
        Previous
      </button>
      <button disabled=currentIndex > cycleDots onClick=nextCallback>
        Next
      </button>
    </div>
  );
;

const App = () => 
  // We just have to keep track of the index by keeping it in the state
  const [state, setState] = useState( currentIndex: 0, slidesToScroll: 0 );

  // To access underlying DOM object for the slider
  const sliderRef = useRef();

  // Trigger next method to show the next slides
  const next = () => 
    sliderRef.current.slickNext();
  ;

  // Trigger previous method to show the previous slides
  const previous = () => 
    sliderRef.current.slickPrev();
  ;

  // Slider Settings
  const settings = 
    slidesToShow: 3,
    dots: false,
    draggable: false,
    slidesToScroll: DESKTOP_SLIDES_SCROLL,
    arrows: false,
    speed: 1300,
    autoplay: false,
    centerMode: false,
    infinite: false,
    afterChange: indexOfCurrentSlide => 
      setState(
        currentIndex: indexOfCurrentSlide,
        slidesToScroll: DESKTOP_SLIDES_SCROLL
      );
    ,
    responsive: [
      
        breakpoint: 992,
        settings: 
          slidesToShow: 2,
          slidesToScroll: TABLET_SLIDES_SCROLL,
          afterChange: indexOfCurrentSlide => 
            setState(
              currentIndex: indexOfCurrentSlide,
              slidesToScroll: TABLET_SLIDES_SCROLL
            );
          
        
      ,
      
        breakpoint: 576,
        settings: 
          slidesToShow: 1,
          slidesToScroll: MOBILE_SLIDES_SCROLL,
          afterChange: indexOfCurrentSlide => 
            setState(
              currentIndex: indexOfCurrentSlide,
              slidesToScroll: MOBILE_SLIDES_SCROLL
            );
          
        
      
    ]
  ;

  return (
    <div className="app">
      <div>
        <h1>Slider Buttons</h1>
        renderArrows(previous, next, state, TOTAL_SLIDES - 1)
      </div>

      /*  Slider */
      <Slider ...settings ref=slider => (sliderRef.current = slider)>
        [...Array(TOTAL_SLIDES)].map((_, index) => 
          return (
            <div className="card" key=index>
              <div className="card-img-top">
                <svg
                  className="svg"
                  
                  
                  xmlns="http://www.w3.org/2000/svg"
                  preserveAspectRatio="xMidYMid slice"
                  focusable="false"
                >
                  <rect   fill="#55595c" />
                  <text x="50%" y="50%" fill="#eceeef" dy=".3em">
                    Image index + 1
                  </text>
                </svg>
              </div>
              <div className="card-body">
                <p className="card-text">
                  This is a wider card with supporting text below.
                </p>
              </div>
            </div>
          );
        )
      </Slider>
    </div>
  );
;

export default App;

【问题讨论】:

【参考方案1】:

看起来一切正常,但禁用了 Next 按钮。我认为它应该已经可以通过更改disabled 条件来检查滑块所在的幻灯片数量。

在提供的代码沙箱中将renderArrow 正文更改为此似乎可行:

  const lastElementsIndex = totalSlides - slidesToScroll;
  return (
    <div>
      <button disabled=currentIndex === 0 onClick=prevCallback>
        Previous
      </button>
      <button disabled=currentIndex > lastElementsIndex onClick=nextCallback>
        Next
      </button>
    </div>
  );

由于初始状态与实际值不同步,具体取决于断点,因此可能存在 cmets 中指出的特殊情况。使用 onReInit 作为非箭头函数应该可以访问 Slick 滑块本身的 this 对象。从那里,可以检索到正确的slidesToScroll

const settings = 
  // ... other settings ...
  onReInit: function() 
    const slidesToScroll = this.slidesToScroll;
    if (slidesToScroll !== state.slidesToScroll) 
      setState(state => ( ...state, slidesToScroll ));
    
  ,
  // ... other settings ...
;

在之前的编辑中,我建议使用onInit - 使用onReInit 还可以在调整窗口大小时进行拍摄。因此,它也应该在调整大小时正确同步状态。

【讨论】:

感谢您的回答。我想问一下currentIndex = 0totalSlides = 2slidesToScroll = 0。所以在这种情况下,3 slides 已经出现,那么lastElementsIndex 的值将是2。下一个按钮不会被禁用,因为我们的条件将返回 false 即 0 &gt; 2 你将如何处理它? slidesToScroll = 0 - 这是什么意思?在我看来,用户根本不应该滚动/看不到任何幻灯片? 对不起,我没听懂你的意思。这是 Codesandbox 链接see here 在此示例中,您可以看到全局变量中的幻灯片总数为3,而在Desktop break-point 中已经出现了 3 张幻灯片,并且前面没有幻灯片。所以应该禁用下一个按钮。 啊,好吧,现在我看到了第二个问题。对不起!在链接代码中,state 与滑块开始接收的状态不同步。 slidesToScroll 仅在滑块更改时更新为正确的值,因此稍后更新。您可以使用 onInit 属性来同步它。我会更新答案【参考方案2】:

这是我为使您的代码正常工作所做的:

const renderArrows = (
  prevCallback,
  nextCallback,
   currentIndex, slidesToScroll ,
  totalSlides
) => 
  let cycleDots = 0;
  if (slidesToScroll !== 0) 
    cycleDots =
      currentIndex === 0 ? 1 : Math.ceil(totalSlides / slidesToScroll);
   else 
    cycleDots = 5;
  

  return (
    <div>
      <button disabled=currentIndex === 0 onClick=prevCallback>
        Previous
      </button>
      <button disabled=currentIndex >= cycleDots onClick=nextCallback>
        Next
      </button>
    </div>
  );
;

我不知道您不想在 if-else 语句中将 'cycleDots' 值设置为什么;但是您应该能够用 totalSlides 为负 1 的任何值替换 5;

【讨论】:

【参考方案3】:

我在 currentIndex > cycleDots 中发现了一个问题。请改成 currentIndex >= cycleDots

【讨论】:

你能帮帮我吗?您知道调整窗口大小时刷新滑块的任何方法吗?

以上是关于Prev 和 Next 按钮在断点处无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

Xdebug 停止工作并且无法在断点处停止

我该如何分页: Prev 4 5 6 7 Next

Flickity carousel:将 prev 和 next 按钮包装在自定义 div 中

使用 jquery 中的每个循环的 Next 和 Prev 按钮功能

禁用轮播 Prev Next 按钮

为啥 jCarousel Next/Prev 按钮在添加项目后未启用