首页 > 解决方案 > setInterval 不在 React 功能组件中

问题描述

我做了一个 React 组件,可以在必要时将内容滚动到页面顶部,如果您查看代码沙箱示例,如果向下滚动一点会显示“GO Top”按钮,如果单击它,页面将滚动到顶部,如果您尝试向下滚动,它总是会将您带到页面顶部。原因是没有清除计时器,因为 intervalId 始终为空。具体来说,如果您查看第 20 行,intervalId 始终为空,我想这可能与关闭有关,但我无法修复它。请帮忙。

谢谢!

代码贴在下面,codesanbox 示例在这里

import React from "react";

const GoTop = ({ scrollStepInPx, delayInMs }) => {
  const [intervalId, setIntervalId] = React.useState(null);
  const [position, setPosition] = React.useState(false);

  React.useEffect(() => {
    document.addEventListener("scroll", () => {
      if (window.scrollY > 200) {
        setPosition(true);
      } else {
        setPosition(false);
      }
    });
    window.scrollTo(0, 0);
  }, []);

  const onScrollStep = () => {
    if (window.pageYOffset === 0) {
      console.log(intervalId); //always null
      clearInterval(intervalId);
      return;
    }
    window.scroll(0, window.pageYOffset - scrollStepInPx);
  };

  const scrollToTop = () => {
    const id = setInterval(onScrollStep, delayInMs);
    setIntervalId(() => id);
  };

  const renderGoTopIcon = () => {
    if (position) {
      return (
        <button className="go-top" onClick={scrollToTop} type="button">
          Go Top
        </button>
      );
    }
    return null;
  };

  return <>{renderGoTopIcon()}</>;
};

export default GoTop;

标签: reactjsreact-hookssetinterval

解决方案


我让它像这样工作,这篇文章有帮助。 https://dmitripavlutin.com/react-hooks-stale-closures/

import React from "react";

const GoTop = ({ scrollStepInPx, delayInMs }) => {
  const [position, setPosition] = React.useState(false);

  React.useEffect(() => {
    document.addEventListener("scroll", () => {
      if (window.scrollY > 200) {
        setPosition(true);
      } else {
        setPosition(false);
      }
    });
    window.scrollTo(0, 0);
  }, []);

  const scrollToTop = () => {
    let intervalId;
    //move the event handle here, NOT use hooked state variable intervalId
    const onScrollStep = () => {
      if (window.pageYOffset === 0) {
        clearInterval(intervalId);
        return;
      }
      window.scroll(0, window.pageYOffset - scrollStepInPx);
    };

    intervalId = setInterval(onScrollStep, delayInMs);
  };

  const renderGoTopIcon = () => {
    if (position) {
      return (
        <button className="go-top" onClick={scrollToTop} type="button">
          Go Top
        </button>
      );
    }
    return null;
  };

  return <>{renderGoTopIcon()}</>;
};

export default GoTop;

推荐阅读