状态确实在滚动条上设置,但是从事件侦听器记录下来,它似乎停留在初始值。
我猜这与在定义副作用时设置scrolling
有关,但是我如何才能从滚动中触发状态更改?我假定的任何窗口事件都一样。
下面是一个代码框示例:https://codesandbox.io/s/react-test-zft3e
const [scrolling, setScrolling] = useState(false);
useEffect(() => {
window.addEventListener("scroll", () => {
console.log(scrolling);
if (scrolling === false) setScrolling(true);
});
}, []);
return (
<>
scrolling: {scrolling}
</>
);
答案 0 :(得分:1)
这意味着那一刻该变量在那一刻也被“捕获”,因为在重新渲染时,您不会重新初始化事件侦听器。
有点像登机时刻的快照。
如果将console.log移到外面,您将看到它在重新渲染时发生变化,并再次设置滚动值。
const [scrolling, setScrolling] = useState(false);
useEffect(() => {
window.addEventListener("scroll", () => {
if (scrolling === false) setScrolling(true);
});
}, []);
console.log(scrolling);
return (
<>
scrolling: {scrolling}
</>
);
答案 1 :(得分:0)
因此,您的匿名函数被锁定在scrolling
的初始值上。这就是闭包在JS中的工作方式,您最好找到一些漂亮的文章,这可能会有些棘手,并且大量依赖于闭包。
到目前为止,这里有3种不同的解决方案:
useEffect(() => {
const scrollHandler = () => {
if (scrolling === false) setScrolling(true);
};
window.addEventListener("scroll", scrollHandler);
return () => window.removeEventListener("scroll", scrollHandler);
}, [scrolling]);
在遵循此路径的同时,请确保您是useEffect
的{{3}}函数。这是一种很好的默认方法,但滚动它可能会影响性能,因为滚动事件经常触发 。
const scrolling = useRef(false);
useEffect(() => {
const handler = () => {
if (scrolling.current === false) scrolling.current = true;
};
window.addEventListener("scroll", handler);
return () => window.removeEventListener("scroll", handler);
}, []);
return (
<>
scrolling: {scrolling}
</>
);
缺点:更改ref不会触发重新渲染。因此,您需要其他一些变量来更改它以触发重新渲染。
(我在这里将其作为首选方式):
useEffect(() => {
const scrollHandler = () => {
setScrolling((currentScrolling) => {
if (!currentScrolling) return true;
return false;
});
};
window.addEventListener("scroll", scrollHandler);
return () => window.removeEventListener("scroll", scrollHandler);
}, []);
请注意,即使是一次性使用效果,也最好返回清理功能 。
PS另外,到目前为止,您还没有将scrolling
设置为false
,因此可以摆脱条件if(scrolling === false)
,但是可以肯定的是,在实际情况下,您可能还会遇到某些问题一样。