【问题标题】:React hooks not updating state for array of images on click eventReact 钩子不会在单击事件上更新图像数组的状态
【发布时间】:2020-10-13 15:25:41
【问题描述】:

我在后台有一个由无头 CMS 填充的图像网格。当用户点击其中一张图片时,他们将被带到图片幻灯片中。

我需要能够在网格中单击图像的索引处打开图像幻灯片。

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 });
    console.log(activeSlide, index, state);
  };

  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(activeSlide, index, state),我会得到...

0 5 {translate: 0, transition: 0.45, activeSlide: 0}

【问题讨论】:

  • setState 是异步的,所以在setState 之后的console.log(activeSlide, index, state); 不会总是你认为的那样
  • 大量代码要阅读!!!!请删除不重要的行以便更容易理解。

标签: arrays reactjs react-hooks


【解决方案1】:

这里有两个(也许更多)主要错误,一个是因为setState is async,您记录了一个陈旧的状态。

其次是useStatesetState钩子,不要合并状态like this.setState in classes,所以这样的代码:setState({ activeSlide: index });实际上会覆盖你的其他状态值:{translate,activeSlide,...}

通常在这种情况下,你需要使用functional updates

const show = (index) => {
  setShowGrid(false);
  setShowSlider(true);
  setState((prev) => {
    console.log(prev);
    return { ...prev, activeSlide: index };
  });
};

【讨论】:

  • 感谢您的帮助,仍然可以使用钩子。我可以看到这是功能更新的正确方法,但它似乎仍然没有更新 activeSlide。所以我的 console.log 读取为 activeSlide: 0 无论点击哪个索引。
  • 你应该做一个可重现的例子,这样的代码太长了,看不懂,请阅读How to create a Minimal, Reproducible Example
猜你喜欢
  • 2019-10-29
  • 1970-01-01
  • 1970-01-01
  • 2018-02-20
  • 1970-01-01
  • 1970-01-01
  • 2021-05-19
  • 1970-01-01
  • 2021-08-24
相关资源
最近更新 更多