React滚动区域仅滚动时显滚动条,滚动到底部触发无限重复事件求助
问题分析
滚动到底部时scroll事件无限触发的核心原因是:滚动条的显示/隐藏改变了容器的可用空间。当isScroll变为false时,滚动条被隐藏,容器内部的可滚动宽度/高度增加,导致内容位置发生偏移,触发新的scroll事件;事件触发后又将isScroll设为true,滚动条重新显示,容器空间再次变化,形成无限循环。
解决方案
1. 预留滚动条空间,避免布局抖动
给滚动容器添加固定内边距(等于滚动条宽度),确保滚动条显示/隐藏时,容器的可用空间不会变化,不会触发额外的scroll事件。
2. 优化防抖函数的使用
用useCallback替代useMemo创建防抖函数,同时在组件卸载时取消防抖任务,防止内存泄漏。
3. 避免重复设置状态
在handleScroll中判断当前isScroll状态,已经为true时不再重复调用setIsScroll(true),减少不必要的重渲染。
修改后的代码
import { useState, useCallback, useEffect } from 'react'; import _ from 'lodash'; const ScrollableContainer = () => { const [isScroll, setIsScroll] = useState(false); // 用useCallback创建防抖函数,保证引用稳定 const handleScrollEnd = useCallback( _.debounce(() => { console.log('Scroll End'); setIsScroll(false); }, 1000), [] ); // 组件卸载时取消防抖任务 useEffect(() => { return () => handleScrollEnd.cancel(); }, [handleScrollEnd]); const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => { console.log('Scroll Start'); // 仅当未处于滚动状态时才更新状态 if (!isScroll) { setIsScroll(true); } handleScrollEnd(); }, [isScroll, handleScrollEnd]); return ( <div style={{ border: '1px solid', width: '500px', height: '200px', overflow: 'auto', display: 'flex', // 预留webkit滚动条宽度,避免布局变化 paddingRight: '17px', boxSizing: 'border-box' }} onScroll={handleScroll} > {/* 自定义滚动条显示逻辑 */} <style jsx>{` div::-webkit-scrollbar { display: ${isScroll ? 'block' : 'none'}; width: 17px; } div::-webkit-scrollbar-thumb { background-color: #ccc; border-radius: 8px; } `}</style> <div style={{ flex: 1 }}> Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words... </div> <div style={{ width: '20px', flexShrink: 0, flexGrow: 0 }} ></div> </div> ); }; export default ScrollableContainer;
关键改动说明
- 预留滚动条空间:通过
paddingRight: 17px和boxSizing: 'border-box',保证滚动条显示/隐藏时容器内部宽度不变,从根源避免了触发额外scroll事件。 - 防抖函数优化:用
useCallback确保防抖函数引用稳定,配合useEffect在组件卸载时取消任务,避免内存泄漏。 - 状态判断优化:在
handleScroll中增加状态检查,避免重复设置相同状态,减少不必要的组件重渲染。
内容的提问来源于stack exchange,提问作者user16223807
相关产品推荐
相关产品推荐

