繁体   English   中英

Reactjs slider 中的意外使用状态行为

[英]Reactjs unexpected useState behaviour in slider

我使用 useEffect 实现了自动播放 Slider(具有三张卡),但手动“上一个”和“前进”按钮无法正常工作。 useState 没有根据需要更新值。 useState 正在改变我无法理解这种随机行为的值。 一旦我单击上一个或向前箭头滑动幻灯片,它就会开始非常快速地更改幻灯片。 自动 slider 运行良好,但我手动滑动而不是使用状态意外更改值。 我如何实现自动和手动(前进和后退箭头),以便 usestate 顺利更改值。

import React, { useState, useRef, useEffect } from "react";
import { Card, Grid } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import { SlideData, SliderImages } from "./SlideData";
import left from "../../../styles/img/homepg/AdmSlider/left.png";
import right from "../../../styles/img/homepg/AdmSlider/right.png";


function NewsSlider() {
  const classes = useStyles();

  const firstRef = useRef(null);
  const secondRef = useRef(null);
  const thirdRef = useRef(null);
  const currentRef = useRef(null);


  const Left = () => {
    setFirst(first <= 0 ? len : first - 1);
    setSecond(second <= 0 ? len : second - 1);
    setThird(third <= 0 ? len : third - 1);
  };

  const Right = () => {
    setFirst(first >= len ? 0 : first + 1);
    setSecond(second >= len ? 0 : second + 1);
    setThird(third >= len ? 0 : third + 1);
  };

  const [first, setFirst] = useState(0);
  const [second, setSecond] = useState(1);
  const [third, setThird] = useState(2);

  const length = SliderImages.length;
  const len = length - 1;
  let timeout;
  
  useEffect(() => {
    setTimeout(() => {
      setFirst(first >= len ? 0 : first + 1);
      setSecond(second >= len ? 0 : second + 1);
      setThird(third >= len ? 0 : third + 1);
       return () => clearTimeout(timeout);
    }, 3000);
  }, [first, second, third]);

  return (
    <>
      <div>
        <Grid container xs={12} className={classes.grid}>
          {" "}
          <div>
            <img
              src={left}
              alt="leftarrow"
              className={classes.left}
              onClick={Left}
            />
          </div>
          <Grid item xs={4} className={classes.card}>
            {SliderImages.map((val, index) => {
              return (
                <div>
                  {index === first && (
                    <Card className={classes.card1}>
                      <img
                        src={val.imgsrc}
                        alt={val.title}
                        className={classes.image}
                        ref={firstRef}
                      />
                    </Card>
                  )}
                  {index === second && (
                    <Card className={classes.card2}>
                      <img
                        src={val.imgsrc}
                        alt={val.title}
                        className={classes.image}
                        ref={secondRef}
                      />
                    </Card>
                  )}
                  {index === third && (
                    <Card className={classes.card3}>
                      <img
                        src={val.imgsrc}
                        alt={val.title}
                        className={classes.image}
                        ref={thirdRef}
                      />
                    </Card>
                  )}
                  <div>
                    <img
                      src={right}
                      alt="rightarrow"
                      className={classes.right}
                      onClick={Right}
                    />
                  </div>
                </div>
              );
            })}
          </Grid>
        </Grid>
      </div>
    </>
  );
}

export default NewsSlider;

问题

您的问题是,当您手动更新firstsecondthird之一而不清除任何先前的超时时,您会开始另一个超时。

此外,当您在 function 主体中将超时定义为 let timeout时,每个渲染周期都会重新声明let timeout; .

解决方案

捕获 timeout ref 并从useEffect回调而不是 timeout 回调中返回清除 function。

useEffect(() => {
  const timeout = setTimeout(() => {
    setFirst(first >= len ? 0 : first + 1);
    setSecond(second >= len ? 0 : second + 1);
    setThird(third >= len ? 0 : third + 1);
  }, 3000);

  return () => clearTimeout(timeout);
}, [first, second, third]);

替代解决方案

将 slider animation 构造为在间隔上,并使用功能 state 更新来处理上一张/下一张幻灯片。 功能 state 更新允许您使用间隔计时器并从上一张幻灯片 state 正确更新,同时还允许您异步手动向左或向右滑动而不中断间隔更新。

const Left = () => {
  setFirst(first => first <= 0 ? len : first - 1);
  setSecond(second => second <= 0 ? len : second - 1);
  setThird(third => third <= 0 ? len : third - 1);
};

const Right = () => {
  setFirst(first => first >= len ? 0 : first + 1);
  setSecond(second => second >= len ? 0 : second + 1);
  setThird(third => third >= len ? 0 : third + 1);
};

const [first, setFirst] = useState(0);
const [second, setSecond] = useState(1);
const [third, setThird] = useState(2);

const length = SliderImages.length || 0;
const len = length - 1;

useEffect(() => {
  const interval = setInterval(() => {
    Right();
  }, 3000);

  return () => clearInterval(interval);
}, []);

我认为您需要在 useState 挂钩中添加箭头 function

  const Left = () => {
    setFirst(first => first <= 0 ? len : first - 1);
    setSecond(second => second <= 0 ? len : second - 1);
    setThird(third => third <= 0 ? len : third - 1);
  };

  const Right = () => {
    setFirst(first => first >= len ? 0 : first + 1);
    setSecond(second => second >= len ? 0 : second + 1);
    setThird(third => third >= len ? 0 : third + 1);
  };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM