为何元素滚动过视口后设为fixed,回滚时无法恢复relative?
问题:滚动切换元素定位状态时回滚无法恢复的原因分析
需求:当元素滚动超过浏览器视口高度后,将其
position设为fixed;回滚至初始视口区域时,恢复为relative。
问题:当前代码在滚动过视口时能成功将元素设为fixed定位,但回滚时fixed状态始终显示为false,无法执行setFixed(false),元素无法恢复relative定位。
相关代码:
const [fixed, setFixed] = useState(() => { return false; }); const scrollHandler = () => { if (window.scrollY > window.innerHeight) { if (!fixed) { setFixed(true); } } else { if (fixed) { setFixed(false); } } }; useEffect(() => { window.addEventListener("scroll", scrollHandler); return () => { window.removeEventListener("scroll", scrollHandler); }; }, []); return ( <> <div style={{ position: `${fixed ? "fixed" : "relative"}` }} className={classes.navigationBelt} ></div> </> );
问题原因
这是闭包陷阱导致的:
useEffect的依赖数组是空的,意味着scrollHandler只会在组件挂载时创建一次,它捕获的是挂载时的fixed状态(也就是初始值false)。- 当滚动超过视口调用
setFixed(true)后,组件重新渲染,fixed状态更新为true,但此时绑定在滚动事件上的还是旧的scrollHandler函数,它内部的fixed依然是最初的false。 - 所以回滚时,即便
window.scrollY <= window.innerHeight,scrollHandler里判断fixed的结果还是false,自然不会执行setFixed(false)。
简单说就是:旧的滚动回调函数一直拿着最初的fixed值,完全不知道状态已经更新了。
内容的提问来源于stack exchange,提问作者Susaksham Jain
相关产品推荐
相关产品推荐

