【问题标题】:Reactjs unexpected useState behaviour in slider滑块中的Reactjs意外useState行为
【发布时间】:2021-05-20 15:54:21
【问题描述】:

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

【问题讨论】:

    标签: javascript reactjs react-hooks use-effect use-state


    【解决方案1】:

    问题

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

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

    解决方案

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

    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]);
    

    替代解决方案

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

    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);
    }, []);
    

    【讨论】:

    • 我得到了答案,正如你所说,我在错误的位置清除了超时return () =&gt; clearTimeout(timeout);
    【解决方案2】:

    我认为你需要在 useState 钩子中添加箭头函数

      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);
      };
    

    【讨论】:

    • 谢谢,但是为什么我们使用回调函数呢?因为我们已经将值分配给第一个第二个和第三个?
    猜你喜欢
    • 2021-07-01
    • 2021-07-29
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2022-07-02
    • 2021-08-01
    • 2021-08-17
    • 2021-02-25
    相关资源
    最近更新 更多