首页 > 解决方案 > 在反应钩子中使用一组图像在图像网格和图像滑块之间切换

问题描述

我正在使用 Nextjs、React 和使用 Sanity 作为 CMS 构建一个项目。

其中一个主要组件将是一个图像库,当您单击图像时,您将打开一个图像滑块库。图像是从 CMS 传入的数组。

它大致基于该网站的工作方式.. https://www.garrodkirkwood.com/projects/

目前,我正在使用一个简单的 setState 切换来隐藏和显示图片库和幻灯片。代码远非完美,因为我意识到发生了很多事情。

我需要什么帮助......

我需要找到一种方法将单击图像的索引传递给事件侦听器,以便在单击图像时打开相同图像上的幻灯片。

我真的希望这是有道理的。

然后,用户将通过单击“显示缩略图”退出幻灯片

从“react”导入反应,{ useState,useEffect,useRef };
从“@emotion/core”导入 { css, jsx };
从“./ImageSliderContent”导入 ImageSliderContent;
从“./ImageGrid.module.css”导入样式;
从“./ImageGrid”导入 ImageGrid;
从“./ImageSlide”导入 ImageSlide;
从“./Arrow”导入箭头;
从“./Dots”导入点;
从“@sanity/image-url”导入 imageUrlBuilder;
从“../../client”导入客户端;

const builder = imageUrlBuilder(client);

const LocationsImageGallery = (props) => {
  常量 { 标题,图像 } = 道具;

  常量图像 = props.image;

  const [状态,setState] = useState({
    翻译:0,
    过渡:0.45,
    活动幻灯片:0,
  });

  常量 [showSlider, setShowSlider] = useState(false);

  常量 { 翻译,过渡,activeSlide,_slides } = 状态;

  常量大小 = useWindowSize();
  常量 transitionRef = useRef();

  函数 useWindowSize() {
    const isClient = typeof window === "object";

    函数 getSize() {
      返回 {
        宽度:是客户端?window.innerWidth :未定义,
      };
    }

    const [windowSize, setWindowSize] = useState(getSize);

    使用效果(()=> {
      如果(!isClient){
        返回假;
      }

      函数句柄调整大小(){
        setWindowSize(getSize());
      }

      window.addEventListener("resize", handleResize);
      return () => window.removeEventListener("resize", handleResize);
    }, []);

    返回窗口大小;
  }

  常量 nextSlide = () => {
    if (activeSlide === images.length - 1) {
      返回集合状态({
        ...状态,
        翻译:0,
        活动幻灯片:0,
      });
    }

    设置状态({
      ...状态,
      活动幻灯片:活动幻灯片+ 1,
      翻译:(activeSlide + 1)* size.width,
    });
  };

  常量 prevSlide = () => {
    if (activeSlide === 0) {
      返回集合状态({
        ...状态,
        翻译:(images.length - 1)* size.width,
        activeSlide: images.length - 1,
      });
    }

    设置状态({
      ...状态,
      活动幻灯片:活动幻灯片 - 1,
      翻译:(activeSlide - 1)* size.width,
    });
  };

  常量显示 = () => {
    设置显示滑块(真);
  };

  常量隐藏 = () => {
    设置显示滑块(假);
  };

  返回 (
    <div className={styles.root}>
      <div className={styles.imageGridContainer}>
        {images.map((图像,索引)=>(
          <div className={styles.imageContainer} onClick={show}>
            <img
              src={builder.image(image).auto("format").width(2000).url()}
              类名={styles.image}
              alt={图片说明}
              键={索引}
            />
            <p className={styles.caption}>{image.caption}</p>
          </div>
        ))}
      </div>

      {显示滑块 && (
        <div className={styles.imageGalleryContainer}>
          <div css={ImageSliderCSS}>
            <ImageSlider 内容
              翻译={翻译}
              过渡={过渡}
              宽度={大小.宽度*图像.长度}
            >
              {images.map((图像,索引)=>(
                <图片幻灯片
                  键={图像 + 索引}
                  内容={builder.image(image).auto("格式").width(2000).url()}
                ></ImageSlide>
              ))}
            </ImageSliderContent>
            <箭头方向=“左”handleClick={prevSlide} />
            <箭头方向=“右”handleClick={nextSlide} />
            <点幻灯片={图像} activeSlide={activeSlide} />
          </div>
          <a href="" onClick={隐藏}>
            显示缩略图
          </a>
        </div>
      )}
    </div>
  );
};

常量 ImageSliderCSS = css`
  位置:相对;
  高度:500px;
  宽度:750px;
  边距:0 自动;
  溢出:隐藏;
`;
导出默认 LocationsImageGallery;

更新了通过索引点击事件

import React, { useState, useEffect, useRef } from "react";
import { css, jsx } from "@emotion/core";
import ImageSliderContent from "./ImageSliderContent";
import styles from "./LocationsImageGallery.module.css";
import ImageGrid from "./ImageGrid";
import ImageSlide from "./ImageSlide";
import Arrow from "./Arrow";
import Dots from "./Dots";
import imageUrlBuilder from "@sanity/image-url";
import client from "../../client";

const builder = imageUrlBuilder(client);

const LocationsImageGallery = (props) => {
  const { caption, image } = props;

  const images = props.image;

  const [state, setState] = useState({
    translate: 0,
    transition: 0.45,
    activeSlide: 0,
  });

  const [showSlider, setShowSlider] = useState(false);
  const [showGrid, setShowGrid] = useState(true);

  const { translate, transition, activeSlide, _slides } = state;

  const size = useWindowSize();
  const transitionRef = useRef();

  function useWindowSize() {
    const isClient = typeof window === "object";

    function getSize() {
      return {
        width: isClient ? window.innerWidth : undefined,
      };
    }

    const [windowSize, setWindowSize] = useState(getSize);

    useEffect(() => {
      if (!isClient) {
        return false;
      }

      function handleResize() {
        setWindowSize(getSize());
      }

      window.addEventListener("resize", handleResize);
      return () => window.removeEventListener("resize", handleResize);
    }, []);

    return windowSize;
  }

  const nextSlide = () => {
    if (activeSlide === images.length - 1) {
      return setState({
        ...state,
        translate: 0,
        activeSlide: 0,
      });
    }

    setState({
      ...state,
      activeSlide: activeSlide + 1,
      translate: (activeSlide + 1) * size.width,
    });
  };

  const prevSlide = () => {
    if (activeSlide === 0) {
      return setState({
        ...state,
        translate: (images.length - 1) * size.width,
        activeSlide: images.length - 1,
      });
    }

    setState({
      ...state,
      activeSlide: activeSlide - 1,
      translate: (activeSlide - 1) * size.width,
    });
  };

  const show = (index) => {
    setShowGrid(false);
    setShowSlider(true);
    setState({ activeSlide: index });
  };

  const hide = () => {
    setShowSlider(false);
    setShowGrid(true);
  };

  return (
    <div className={styles.root}>
      <div className={styles.header}>
        <a href="/locations">X</a>
      </div>
      {showGrid && (
        <div className={styles.imageGrid}>
          <div className={styles.imageGridContainer}>
            {images.map((image, index, caption) => (
              <div className={styles.imageContainer} onClick={() => show(index)}>
                <img
                  src={builder.image(image).auto("format").width(2000).url()}
                  className={styles.image}
                  alt={image.caption}
                  key={index}
                />
                <p className={styles.caption}>{image.caption}</p>
              </div>
            ))}
          </div>
        </div>
      )}

      {showSlider && (
        <div className={styles.imageGalleryContainer}>
          <div className={styles.imageSlider}>
            <ImageSliderContent
              translate={translate}
              transition={transition}
              width={size.width * images.length}
            >
              {images.map((image, index, caption) => (
                <>
                  <ImageSlide
                    key={image + index}
                    content={builder.image(image).auto("format").url()}
                  ></ImageSlide>
                </>
              ))}
            </ImageSliderContent>
            <Arrow direction="left" handleClick={prevSlide} />
            <Arrow direction="right" handleClick={nextSlide} />
          </div>
          <div className={styles.infoBar}>
            <p className={styles.infoCaption}>
              Locations / <span>{image.caption}</span>
            </p>
            <a href="" onClick={hide} className={styles.infoThumbnails}>
              Show Thumbnails
            </a>
          </div>
        </div>
      )}
    </div>
  );
};

export default LocationsImageGallery;

这个版本,好像没有更新状态,activeSlide的console.log和状态...

LocationsImageGallery.js?0692:94 {activeSlide: 8, state: {…}} activeSlide: 8 state: activeSlide: 0 transition: 0.45 translate: 0 proto: Object proto: Object

标签: arraysreactjsreact-hooksnext.js

解决方案


您能否尝试将自定义属性添加到 img 元素,并将索引作为值,例如

data-index = {index}

然后,您可以回到原来的方式来执行 onClick 处理程序

onClick={show}

然后,您的 show 函数可以像这样获取该自定义属性:

  const show = (e) => {
   setShowGrid(false);
   setShowSlider(true);
   setState({ activeSlide: e.target.getAttribute("data-index") });
 };

推荐阅读