根据索引显示或隐藏轮播项目 |反应

Show or hide Carousel item depending on index | React

我正在使用 React Slick centerMode 作为我的图像轮播并尝试一次显示 3 张图像。另外,我想仅在居中(当前索引)时显示图像的标题。问题是它会同时显示所有标题。如何隐藏左右图片的标题,只显示中间图片的标题?

What I made

What I want

我的代码:

function ImageSlider({ imageList }) {
  const [currentSlide, setCurrentSlide] = useState(0);
  const [indexSlide, setIndexSlide] = useState(0);

  function ShowTitle(title) {
      // If the index is on focused
      if (currentSlide === indexSlide) {
        return <p>{title}</p>;
      }
      // Otherwise return empty
      return <p></p>;
    }
  
  function handleAfterChange(index) {
      setIndexSlide(index);
      const centerSlide = document.querySelector(
        "div.slick-slide.slick-active.slick-center.slick-current"
      );
      setCurrentSlide(parseInt(centerSlide.getAttribute("data-index")));
  }

  return (
    <Slider
      className="center"
      centerMode={true}
      infinite={true}
      autoplay={true}
      autoplaySpeed={2000}
      slidesToShow={3}
      slidesToScroll={1}
      afterChange={handleAfterChange}
    >
      {imageList.map((image) => (
        <div key={image.id} id={image.id}>
          <img
            alt="image"
            width={270}
            height={120}
            src={image.url}
          />
// I want this to show when it's centered, and hidden when it's not centred
          <ShowTitle title={image.title} key={image.id} /> 
        </div>
      ))}
    </Slider>
  );
}

当索引号 2(共 5 个)居中时的轮播 html:

<div class="slick-list" style="padding:0px 60px">
  <div class="slick-track" style="width: 3640px; opacity: 1; transform: translate3d(-1040px, 0px, 0px); transition: -webkit-transform 500ms ease 0s;">
    <div data-index="-4" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
    <div data-index="-3" tabindex="-1" class="slick-slide slick-center slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
    <div data-index="-2" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
    <div data-index="-1" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
    <div data-index="0" class="slick-slide" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
    <div data-index="1" class="slick-slide slick-active" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
    <div data-index="2" class="slick-slide slick-active slick-center slick-current" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
    <div data-index="3" class="slick-slide slick-active" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
    <div data-index="4" class="slick-slide" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
    <div data-index="5" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
    <div data-index="6" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
    <div data-index="7" tabindex="-1" class="slick-slide slick-center slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
    <div data-index="8" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
    <div data-index="9" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
  </div>
</div>

问题

您的问题源于您尝试对状态变量执行逻辑的方式。除了引用它们之外,您的状态和 ShowTitle 组件之间没有任何联系。每当您翻阅轮播时,您都在更改 both 状态变量,因此它们将 始终 相同。

不幸的是,您为轮播选择的库迫使您依赖索引,因此我建议您使用 map 函数上的 index 参数将索引分配给您的 ShowTitle 组件。 注意:如果您尝试对键使用索引,React 会警告您,因为这可能会导致奇怪的行为。为此继续使用 id。

解决方案

function ImageSlider({ imageList }) {
  const [currentSlide, setCurrentSlide] = useState(0);

  // Since this is a component, you need to destructure props
  function ShowTitle({ title, id }) {
      // If the index is on focused
      if (id === currentSlide) {
        return <p>{title}</p>;
      }
      // Otherwise return empty
      return <p></p>;
    }
  
  // I'm not sure what props you're going to get here; it'll be whatever
  // the Slider provides you with when afterChange is fired
  function handleAfterChange(index) {
      setCurrentSlide(index);
  }

  return (
    <Slider
      className="center"
      centerMode={true}
      infinite={true}
      autoplay={true}
      autoplaySpeed={2000}
      slidesToShow={3}
      slidesToScroll={1}
      afterChange={handleAfterChange}
    >
      {/* Add index argument to map here (this is provided automatically) */}
      {imageList.map((image, index) => (
        <div key={image.id} id={image.id}>
          <img
            alt="image"
            width={270}
            height={120}
            src={image.url}
          />
          {/* Add index prop to ShowTitle */}
          <ShowTitle title={image.title} index={index} /> 
        </div>
      ))}
    </Slider>
  );
}

一个建议

如果您想对此进行优化,您可以完全取消 ShowTitle 组件并有条件地呈现您的 p 标签。

...
{index === currentSlide ? <p>{image.title}</p> : <p></p>}
...

这一切的原因

当 react 呈现您的所有代码时,它将调用 ShowTitle 组件,因为它实际上是您编写此方式的组件。函数组件只是一个函数,它接受 props 作为参数,returns 一些 JSX。因为它会在地图上的每个循环中调用它,所以它会比较当前设置的状态(在用户执行某些操作之前不会改变 afterChange)所以它每次都会比较相同的状态变量,这就是为什么所有的标题显示。当您确实调用 afterChange 时,它总是将两个状态变量设置为相同的东西,并且更改状态会导致重新渲染。这就是所有标题始终存在的原因。

要纠正这个问题,您需要将特定幻灯片的呈现与轮播中当前选定的索引相关联,并且您需要您的组件知道它在轮播中的位置(通过map 函数)以便能够与当前选定的索引进行比较。