setTimeout内部setInterval反应

时间:2020-08-03 05:35:03

标签: reactjs typescript settimeout setinterval

我正在尝试使轮播与某些动画互动。我已经设置了一些状态来更改当前渲染的子级+是否应显示淡入淡出过渡。

我遇到了一些问题

  1. 有时用户会更改页面,并且setTimeoutnext()内部的previous()会触发,并且在卸载组件后,我会收到有关状态更改的控制台错误。 / li>
  2. 用户只需向垃圾邮件中的下一个/上一个按钮发送垃圾邮件,整个轮播就会使某人癫痫发作

我已经将start()stop()重构为使用intervalRef,并且我感到将超时转换为timeoutRef可以解决这两个问题,但是我不确定如何

type Props = {
  interval?: number;
  timeout?: number;

  children: React.ReactNodeArray;
}
const Carousel: React.FC = ({ interval = 4000, timeout = 500, children }) => {
  const [fade, setFade] = useState(true); // Start fade animation
  const [childIndex, setChildIndex] = useState(0); // Which child to render

  const intervalRef = useRef<NodeJS.Timer | null>(null);

  const next = useCallback(() => {
    setFade(false); // do the fade out animation

    setTimeout(() => {
      setChildIndex((pIndex) => (pIndex + 1) % children.length); // update which to display

      setFade(true); // display fade in animation
    }, timeout);
  }, [timeout, children]);
  const previous = useCallback(() => {
    setFade(false); // do the fade out animation

    setTimeout(() => {
      setChildIndex((pIndex) => ((pIndex - 1) + children.length) % children.length); // update which to display

      setFade(true); // display fade in animation
    }, timeout);
  }, [timeout, children]);

  const start = useCallback(() => {
    if (intervalRef.current === null) {
      intervalRef.current = setInterval(next, interval);
    }
  }, [next, interval]);
  const stop = useCallback(() => {
    if (intervalRef.current !== null) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
  }, []);

  useEffect(() => {
    start();

    return () => stop();
  }, [start, stop]);

  return (
    <>
      <Fade in={fade} timeout={timeout} onMouseEnter={stop} onMouseLeave={start}> // don't switch item on hover
        <div>
          {children[childIndex]}
        </div>
      </Fade>
      <Button onClick={previous}>Previous</Button>
      <Button onClick={next}>Next</Button>
    </>
  )
}

1 个答案:

答案 0 :(得分:-1)

setTimeout应该真正在Effect内部调用,以便在卸载组件时可以clearTimeout

遵循以下原则:

useEffect(() => {
  const tId = setTimeout(() => {
     // do stuff
  }, 500);
  return () => clearTimeout(tId);
}, []);

要处理“键盘粉碎用户”问题,请查看debouncingthrottling您的事件处理程序