如何在React中正确添加窗口滚动事件监听器实现元素动画
解决React导航栏滚动时Logo宽度变化的问题
原代码存在的问题
- 直接操作DOM(
document.querySelector("#logo"))不符合React状态驱动UI的设计理念,易引发状态与UI不一致的问题 useEffect依赖scrollY状态,每次滚动更新scrollY都会重复绑定/解绑滚动事件,造成不必要的性能损耗- 滚动事件触发过于频繁,未做节流处理,可能影响页面运行性能
优化方案
- 用React状态控制UI:定义
isScrolled状态标记是否滚动超过200px,通过状态控制Logo样式,替代直接操作DOM - 优化事件绑定逻辑:仅在组件挂载时绑定一次滚动事件,卸载时解绑,将
useEffect依赖项设为空数组 - 添加节流处理:限制滚动事件的触发频率,降低性能消耗
- 用useRef保存节流函数:避免组件重渲染时重复创建函数
优化后的完整代码
import { useState, useEffect, useRef } from "react"; import { Link } from "react-router-dom"; import logo from "../assets/images/logo.png"; import "../styles/navbar.scss"; const Navbar = () => { const [isScrolled, setIsScrolled] = useState(false); const throttledScrollHandler = useRef(null); const handleScroll = () => { setIsScrolled(window.scrollY > 200); }; useEffect(() => { // 实现节流:100ms内仅执行一次滚动逻辑 throttledScrollHandler.current = throttle(handleScroll, 100); window.addEventListener("scroll", throttledScrollHandler.current); // 组件卸载时解绑事件 return () => { window.removeEventListener("scroll", throttledScrollHandler.current); }; }, []); // 节流函数封装 function throttle(func, delay) { let lastCall = 0; return function(...args) { const now = Date.now(); if (now - lastCall >= delay) { func.apply(this, args); lastCall = now; } }; } return ( <header> <nav> <Link to="/" className="nav-link"> Grupuri </Link> <Link to="/" className="nav-link"> Despre noi </Link> <Link to="/" className="nav-link"> Live </Link> </nav> {/* 根据状态动态设置Logo宽度,添加过渡动画提升体验 */} <img src={logo} alt="Maranata Lupeni" id="logo" style={{ width: isScrolled ? "0" : "150px", transition: "width 0.3s ease" }} /> <nav> <Link to="/" className="nav-link"> Rugaciune </Link> <Link to="/" className="nav-link"> Credinta </Link> <Link to="/" className="nav-link"> Contact </Link> </nav> </header> ); }; export default Navbar;
额外优化建议
可以将样式逻辑迁移到SCSS文件中,通过类名切换控制Logo宽度,实现JS与样式的解耦:
#logo { width: 150px; transition: width 0.3s ease; } #logo.scrolled { width: 0; }
对应JSX修改为:
<img src={logo} alt="Maranata Lupeni" id="logo" className={isScrolled ? "scrolled" : ""} />
内容的提问来源于stack exchange,提问作者Alin-Alexandru Peter
相关产品推荐
相关产品推荐

