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

Prev and Next button is not working correctly at the breakpoints

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

我为断点定义的设置:

992 > slidesToScroll = 3

577 - 991 slidesToScroll = 2

0 - 576 slidesToScroll = 1

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

我的问题是什么?

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

看到这个:

Codesandbox: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"
                  width="100%"
                  height="225"
                  xmlns="http://www.w3.org/2000/svg"
                  preserveAspectRatio="xMidYMid slice"
                  focusable="false"
                >
                  <rect width="100%" height="100%" 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;

我发现 {currentIndex > cycleDots} 有问题。请更改为 {currentIndex >= cycleDots}

看起来一切正常,但禁用了 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>
  );

由于断点不同,初始状态与实际值不同步,可能存在评论中指出的特殊情况。使用 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 也有利于 window 调整大小。因此它也应该在调整大小时正确同步状态。

为了让您的代码正常工作,我做了以下工作:

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;