Mug*_*g84 9 javascript scroll event-handling reactjs
我试图检测滚动事件是向上还是向下,但我找不到解决方案。
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;
Run Code Online (Sandbox Code Playgroud)
基本上它总是被检测为“down”,因为yinhandleNavigation总是 0。如果我检查 DevTool 中的y状态,状态会更新,但handleNavigation 不会。
任何建议我做错了什么?
谢谢你的帮助
SMA*_*KSS 26
这是因为你定义一个useEffect()没有任何依赖关系,所以你useEffect()只运行一次,它从来没有要求handleNavigation()上y的变化。要解决此问题,您需要添加y到您的依赖项数组中,以useEffect()在y值发生更改后通知您的运行。然后,你需要另一个更改在你的代码,你在哪里试图初始化效果y与window.scrollY,所以您应该在你做到这一点useState(),如:
const [y, setY] = useState(window.scrollY);
useEffect(() => {
window.addEventListener("scroll", (e) => handleNavigation(e));
return () => { // return a cleanup function to unregister our function since its gonna run multiple times
window.removeEventListener("scroll", (e) => handleNavigation(e));
};
}, [y]);
Run Code Online (Sandbox Code Playgroud)
如果由于某种原因 window 可能在那里不可用或者您不想在此处执行此操作,则可以在两个单独的useEffect()s 中执行此操作。
所以你的useEffect()s 应该是这样的:
useEffect(() => {
setY(window.scrollY);
}, []);
useEffect(() => {
window.addEventListener("scroll", (e) => handleNavigation(e));
return () => { // return a cleanup function to unregister our function since its gonna run multiple times
window.removeEventListener("scroll", (e) => handleNavigation(e));
};
}, [y]);
Run Code Online (Sandbox Code Playgroud)
在我自己实施这个解决方案之后。我发现有一些注意事项应该应用于此解决方案。因此,由于handleNavigation()将y直接更改值,我们可以忽略y作为我们的依赖项,然后将其handleNavigation()作为依赖项添加到我们的useEffect(),然后由于此更改我们应该优化handleNavigation(),因此我们应该使用useCallback()它。那么最终的结果将是这样的:
const [y, setY] = useState(window.scrollY);
const handleNavigation = useCallback(
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);
}, [y]
);
useEffect(() => {
setY(window.scrollY);
window.addEventListener("scroll", handleNavigation);
return () => {
window.removeEventListener("scroll", handleNavigation);
};
}, [handleNavigation]);
Run Code Online (Sandbox Code Playgroud)
在@RezaSam 发表评论后,我注意到我在记忆版本中犯了一个很小的错误。我handleNavigation在另一个箭头函数中调用的地方,我发现(通过浏览器开发工具,事件侦听器选项卡)在每个组件重新渲染时,它将向 注册一个新事件,window因此它可能会破坏整个事情。
工作演示:
毕竟,在这种情况下,我最终认为memoization将帮助我们注册单个事件,以识别滚动方向,但在打印控制台时并未完全优化,因为我们在handleNavigation函数内部进行了安慰,没有其他方法可以解决在当前实现中打印所需的控制台。
所以,我意识到每次我们想要检查新位置时,都有一种更好的方法来存储最后一页的滚动位置。同样为了摆脱大量的向上滚动和向下滚动的安慰,我们应该定义一个阈值(使用去抖动方法)来触发滚动事件更改。所以我只是在网上搜索了一下,最后得到了这个非常有用的要点。然后在它的启发下,我实现了一个更简单的版本。
这是它的外观:
const [scrollDir, setScrollDir] = useState("scrolling down");
useEffect(() => {
const threshold = 0;
let lastScrollY = window.pageYOffset;
let ticking = false;
const updateScrollDir = () => {
const scrollY = window.pageYOffset;
if (Math.abs(scrollY - lastScrollY) < threshold) {
ticking = false;
return;
}
setScrollDir(scrollY > lastScrollY ? "scrolling down" : "scrolling up");
lastScrollY = scrollY > 0 ? scrollY : 0;
ticking = false;
};
const onScroll = () => {
if (!ticking) {
window.requestAnimationFrame(updateScrollDir);
ticking = true;
}
};
window.addEventListener("scroll", onScroll);
console.log(scrollDir);
return () => window.removeEventListener("scroll", onScroll);
}, [scrollDir]);
Run Code Online (Sandbox Code Playgroud)
我将简单地从上到下解释每个代码块。
所以我只是用初始值定义了一个阈值点,0每当滚动向上或向下时,它都会进行新的计算,如果您不想立即计算新的页面偏移量,可以增加它。
然后scrollY我决定使用pageYOffset哪个在交叉浏览中更可靠,而不是使用。
在updateScrollDir函数中,我们将简单地检查是否满足阈值,然后如果满足我将根据当前和上一页的偏移量指定滚动方向。
其中最重要的部分是onScroll功能。我只是用来requestAnimationFrame确保我们在滚动后完全呈现页面后计算新的偏移量。然后使用ticking标志,我们将确保我们只在每个requestAnimationFrame.
最后,我们定义了我们的监听器和我们的清理函数。
然后scrollDir状态将包含实际的滚动方向。
工作演示:
| 归档时间: |
|
| 查看次数: |
9286 次 |
| 最近记录: |