React结合Tailwind实现滚动切换导航栏背景色及代码排查
问题解答
一、现有代码存在的问题
- 状态读取逻辑错误:
handleScroll中调用setScrollPosition后,状态更新是异步的,后续判断逻辑读取的scrollPosition是闭包缓存的旧值,不是当前最新的滚动位置,会导致样式切换时机不准。 - 事件绑定逻辑错误:
useEffect没有传入依赖数组,每次组件重渲染都会重新绑定滚动事件、移除旧事件,既造成不必要的性能开销,也存在内存泄漏风险。 - 冗余状态过多:仅需要判断滚动位置是否在顶部阈值范围内即可,单独存储
scrollPosition数值会触发无意义的组件重渲染。 - 缺少性能优化:滚动事件触发频率极高(可达每秒数十次),未做节流处理的话会高频触发setState,很容易导致页面卡顿。
- 判断逻辑冗余:三个判断分支中
scrollPosition < 10已经包含scrollPosition == 0的场景,重复判断没有意义。
二、更优实现方案
优化方向对应上述问题调整即可,不需要引入额外复杂依赖:
- 移除冗余的
scrollPosition状态,仅保留控制导航栏样式的布尔状态 - 给
useEffect传入空依赖数组,仅在组件挂载时绑定一次滚动事件,卸载时正常移除 - 事件回调中直接使用实时获取的
window.scrollY值做判断,不依赖闭包中的旧状态 - 用
requestAnimationFrame做滚动事件节流,和浏览器渲染帧对齐,保证性能的同时不会出现样式更新滞后 - 给导航栏加Tailwind自带的颜色过渡类,让背景色切换更自然,不会生硬跳变
优化后的完整代码如下:
import { useState, useEffect, useRef } from 'react'; export default function Home(props) { const [isTop, setIsTop] = useState(true); // rAF节流标记,用ref存储不会触发重渲染 const ticking = useRef(false); const handleScroll = () => { if (!ticking.current) { window.requestAnimationFrame(() => { const scrollY = window.scrollY; // 保留原逻辑10px的阈值,可根据需求自行调整 setIsTop(scrollY < 10); ticking.current = false; }); ticking.current = true; } }; useEffect(() => { window.addEventListener('scroll', handleScroll); // 初始化时执行一次,处理页面刷新后初始位置不在顶部的场景 handleScroll(); return () => { window.removeEventListener('scroll', handleScroll); }; }, []); return ( <div className={`transition-colors duration-300 ${isTop ? 'bg-blue-500' : 'bg-red-500'}`}> {/* 导航栏内容 */} </div> ); }
如果项目本身已经引入了react-use、usehooks-ts这类工具hook库,也可以直接用库封装好的滚动相关hook,不用自己手写事件绑定逻辑,上述手写版本的性能和可靠性已经能满足绝大多数业务场景需求。
内容的提问来源于stack exchange,提问作者Nasem
相关产品推荐
相关产品推荐

