问题描述
这是我的代码
const [isScrollLocked,setIsScrollLocked] = useState(false)
const handleScroll = (section,scrollLocked) => {
if (!scrollLocked) {
document.getElementById(section).scrollIntoView()
document.querySelector('body').classList.add('overflow-hidden')
setIsScrollLocked(true)
setTimeout(() => {
document.querySelector('body').classList.remove('overflow-hidden')
setIsScrollLocked(false)
},1500)
}
}
useEffect(() => {
const handler = (e) => handleScroll(nextSection,isScrollLocked)
document.addEventListener('scroll',handler)
// cleanup callback,that will be called before the effect runs again
return () => document.removeEventListener('scroll',handler)
},[nextSection,isScrollLocked])
似乎在useEffect之后运行的回调清理正在进行,因此handleScroll内部的scrollIntoView永远不会发生。但是,如果我在console.log触发之前或之后触发。如果删除回调,scrollIntoView会正常工作,但是我会堆积成千上万的滚动事件并使应用程序崩溃。
如果有人能弄清为什么回调清理会产生这种效果,并向我解释一下,我真的很想知道。
解决方法
问题在于您的setTimeout
和useEffect
是如何相互作用的。每当useEffect
更改时,您的isScrollLocked
就会运行。由于您的setTimeout
正在更改isScrollLocked
,因此看起来useEffect
在1.5秒后开始运行。
尝试删除isScrollLocked
观察者:
const [isScrollLocked,setIsScrollLocked] = useState(false);
const handleScroll = (section) => {
if (!isScrollLocked) {
document.getElementById(section).scrollIntoView();
document.querySelector("body").classList.add("overflow-hidden");
setIsScrollLocked(true);
setTimeout(() => {
document.querySelector("body").classList.remove("overflow-hidden");
setIsScrollLocked(false);
},1500);
}
};
useEffect(() => {
const handler = (e) => handleScroll(nextSection);
document.addEventListener("scroll",handler);
// cleanup callback,that will be called before the effect runs again
return () => document.removeEventListener("scroll",handler);
});
这里是链接:https://codesandbox.io/s/react-hooks-counter-demo-forked-7n064