React Hooks,如何实现useHideOnScroll钩子?

时间:2019-03-06 12:22:40

标签: reactjs state react-hooks

const shouldHide = useHideOnScroll();
return shouldHide ? null : <div>something</div>

useHideOnScroll行为不应在每次滚动时都返回更新的值,而应仅在发生更改时才返回。

伪逻辑如下:

if (scrolledDown && !isHidden) {
        setIsHidden(true);
      } else if (scrolledUp && isHidden) {
        setIsHidden(false);
      }

换句话说,如果向下滚动而不是隐藏,则隐藏。如果向上滚动并隐藏,则取消隐藏。但是,如果向下滚动并隐藏,则不执行任何操作;或者向上滚动但不隐藏,则不执行任何操作。

如何用钩子实现呢?

4 个答案:

答案 0 :(得分:2)

在React(16.8.0+)中使用钩子


import React, { useState, useEffect } from 'react'

function getWindowDistance() {
    const { pageYOffset: vertical, pageXOffset: horizontal } = window
    return {
        vertical,
        horizontal,
    }
}

export default function useWindowDistance() {
    const [windowDistance, setWindowDistance] = useState(getWindowDistance())

    useEffect(() => {
        function handleScroll() {
            setWindowDistance(getWindowDistance())
        }

        window.addEventListener('scroll', handleScroll)
        return () => window.removeEventListener('scroll', handleScroll)
    }, [])

    return windowDistance
}

答案 1 :(得分:1)

您需要使用window.addEventListener和https://reactjs.org/docs/hooks-custom.html指南。

这是我的工作示例:

import React, { useState, useEffect } from "react";

const useHideOnScrolled = () => {
  const [hidden, setHidden] = useState(false);

  const handleScroll = () => {
    const top = window.pageYOffset || document.documentElement.scrollTop;
    setHidden(top !== 0);
  };

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return hidden;
};

export default useHideOnScrolled;

实时演示:https://codesandbox.io/s/w0p3xkoq2l?fontsize=14

我认为useIsScrolled()或类似的名称会更好

答案 2 :(得分:1)

这里:

[dsivaji@FADL155 ~]$ cat debug_record.txt
==============================================================================
20190307 10:48:34.845 - INFO - + START SUITE: S SUITE [ ]
==============================================================================
20190307 10:48:34.858 - INFO - +- START SUITE: S SUITE.Dinesh [ ]
==============================================================================
20190307 10:48:34.859 - INFO - +-- START TEST: DEMO [ ]
------------------------------------------------------------------------------
20190307 10:48:34.860 - INFO - +--- START KW: BuiltIn.Log [ "Testcase start" ]
20190307 10:48:34.860 - INFO - "Testcase start"
20190307 10:48:34.861 - INFO - +--- END KW: BuiltIn.Log (1)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20190307 10:48:34.861 - INFO - +--- START KW: test.Sample [ ]
20190307 10:48:34.862 - INFO - THIS IS INSIDE SAMPLE
20190307 10:48:34.863 - INFO - +--- END KW: test.Sample (1)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20190307 10:48:34.863 - INFO - +--- START KW: BuiltIn.Log [ "Testcase end" ]
20190307 10:48:34.864 - INFO - "Testcase end"
20190307 10:48:34.865 - INFO - +--- END KW: BuiltIn.Log (1)
------------------------------------------------------------------------------
20190307 10:48:34.866 - INFO - +-- END TEST: DEMO (7)
------------------------------------------------------------------------------
20190307 10:48:34.869 - INFO - +- END SUITE: S SUITE.Dinesh (22)
==============================================================================
20190307 10:48:34.871 - INFO - + END SUITE: S SUITE (141)
==============================================================================

您可能会担心const useHideOnScroll = () => { const prevScrollY = React.useRef<number>(); const [isHidden, setIsHidden] = React.useState(false); React.useEffect(() => { const onScroll = () => { setIsHidden(isHidden => { const scrolledDown = window.scrollY > prevScrollY.current!; if (scrolledDown && !isHidden) { return true; } else if (!scrolledDown && isHidden) { return false; } else { prevScrollY.current = window.scrollY; return isHidden; } }); }; console.log("adding listener"); window.addEventListener("scroll", onScroll); return () => { window.removeEventListener("scroll", onScroll); }; }, []); return isHidden; }; const Navbar = () => { const isHidden = useHideOnScroll(); console.info("rerender"); return isHidden ? null : <div className="navbar">navbar</div>; }; export default Navbar; 会通过始终返回一些新的状态值而在每个setIsHidden上重新呈现,但是onScroll中的setter很聪明,仅当值具有真的改变了。

此外,您的useState我已经向其中添加了一个类)不应在布局出现时更改布局,否则您的代码段将陷入无限循环中。这也是适合的样式:

.navbar

完整代码沙箱:https://codesandbox.io/s/13kr4xqrwq

答案 3 :(得分:0)

经过几个小时的摇晃,这就是我的想法。

const useHideOnScroll = () => {
  const [isHidden, setIsHidden] = useState(false);
  const prevScrollY = useRef<number>();

  useEffect(() => {
    const onScroll = () => {
      const scrolledDown = window.scrollY > prevScrollY.current!;
      const scrolledUp = !scrolledDown;

      if (scrolledDown && !isHidden) {
        setIsHidden(true);
      } else if (scrolledUp && isHidden) {
        setIsHidden(false);
      }

      prevScrollY.current = window.scrollY;
    };

    window.addEventListener("scroll", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
    };
  }, [isHidden]);

  return isHidden;
};

用法:

const shouldHide = useHideOnScroll();
return shouldHide ? null : <div>something</div>

它仍然不是最佳选择,因为我们在onScroll更改时重新分配了isHidden。其他所有内容都显得过于骇人听闻,没有记载。我真的很想找到一种方法来完成上述任务,而无需重新分配onScroll。如果您知道方法,请发表评论:)