我试图检测滚动事件是向上还是向下,但我找不到解决方案。
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
const Navbar = ({ className }) => {
const [y, setY] = useState(0);
const handleNavigation = (e) => {
const window = e.currentTarget;
if (y > window.scrollY) {
console.log("scrolling up");
} else if (y < window.scrollY) {
console.log("scrolling down");
}
setY(window.scrollY);
};
useEffect(() => {
setY(window.scrollY);
window.addEventListener("scroll", (e) => handleNavigation(e));
}, []);
return (
<nav className={className}>
<p>
<i className="fas fa-pizza-slice"></i>Food finder
</p>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
</nav>
);
};
export default Navbar;
基本上它总是被检测为“关闭”,因为 y
在 handleNavigation
中始终为 0。如果我检查 DevTool 中的状态, y
中的状态更新 handleNavigation
没有。
有什么建议我做错了什么吗?
谢谢你的帮助
原文由 Mugg84 发布,翻译遵循 CC BY-SA 4.0 许可协议
TLDR;
由于这个答案引起了一些关注,所以我刚刚基于这个答案开发了一个npm包,让大家在他们的项目中将它作为一个独立的包/库来使用。因此,如果您只想立即使用某些东西,您可以将它添加到您的项目中:
或者 在此处阅读更多相关信息。
答案及其描述
This is because you defined a
useEffect()
without any dependencies , so youruseEffect()
will only run once, and it never callshandleNavigation()
ony
变化。要解决此问题,您需要将y
添加到依赖项数组,以告诉您的useEffect()
在y
值更改后运行。然后你需要另一个更改才能在你的代码中生效,你正在尝试用 --- 初始化你的y
window.scrollY
,所以你应该在你的useState()
中执行此操作---
喜欢:如果由于某种原因
window
在那里不可用或者你不想在这里做,你可以在两个单独的useEffect()
中完成。所以你的
useEffect()
应该是这样的:更新(工作解决方案)
在我自己实施这个解决方案之后。我发现有一些注释应该应用于此解决方案。 So since the
handleNavigation()
will changey
value directly we can ignore they
as our dependency and then addhandleNavigation()
as a dependency to ouruseEffect()
,那么由于这个变化我们应该优化handleNavigation()
,所以我们应该使用useCallback()
。那么最后的结果会是这样的:在@RezaSam 发表评论后,我注意到我在记忆版本中犯了一个微小的错误。我在另一个箭头函数中调用
handleNavigation
的地方,我发现(通过浏览器开发工具,事件侦听器选项卡)在每个组件重新渲染中都会将一个新事件注册到window
所以它可能毁了整个事情。工作演示:
最终优化方案
毕竟,我最终认为这种情况下的 记忆 将帮助我们注册单个事件,识别滚动方向,但它在打印控制台时并未完全优化,因为我们在
handleNavigation
函数中进行安慰,并且在当前实现中没有其他方法可以打印所需的控制台。所以,我意识到每次我们想要检查新位置时,都有一种更好的方法来存储最后一页滚动位置。同样为了摆脱大量的 向上 滚动和向下 滚动的安慰,我们应该定义一个阈值 (使用去抖动方法) 来触发滚动事件的变化。所以我只是在网上搜索了一下,最后得到了这个非常有用的 要点。然后在它的启发下,我实现了一个更简单的版本。
这是它的样子:
这个怎么运作?
我将简单地从上到下解释每个代码块。
所以我刚刚定义了一个初始值为
0
的阈值点,然后每当滚动向上或向下时,它都会进行新的计算,如果你不想立即计算新的页面偏移量,你可以增加它.然后而不是使用
scrollY
我决定使用pageYOffset
这在交叉浏览中更可靠。在
updateScrollDir
函数中,我们将简单地检查是否满足阈值,如果满足,我将根据当前页面和上一页偏移指定滚动方向。其中最重要的部分是
onScroll
函数。我刚刚使用requestAnimationFrame
来确保我们在页面滚动后完全呈现后计算新的偏移量。然后使用ticking
标志,我们将确保我们只在每个requestAnimationFrame
中运行一次事件监听器回调。最后,我们定义了监听器和清理函数。
然后
scrollDir
状态将包含实际的滚动方向。工作演示: