React中onScroll事件未触发,无限滚动功能实现遇阻求助
React无限滚动onScroll事件不触发的排查与修复
核心问题拆解
- 目标滚动元素不存在:你在
useEffect里尝试获取id="scroll_div"的元素,但返回的JSX中没有任何元素设置该id,导致document.getElementById("scroll_div")返回null,无法绑定scroll事件。 - 滚动容器无固定高度:MUI的Box设置了
overflow: auto,但如果没有固定高度(如height: 100vh),容器会自适应内容高度,不会出现滚动条,自然无法触发onScroll。 - 数据更新逻辑错误:
fetchPosts中直接用setData(newPosts)替换原有数据,而非追加,会覆盖之前的帖子,不符合无限滚动的预期。 - 事件绑定冗余:同时在Box上设置
onScroll={handleScroll}和手动绑定DOM事件,属于重复操作,且后者因元素不存在完全失效。
修复后的完整代码
import { Box } from "@mui/material"; import Post from "./Post"; import About from "./About"; import { useEffect, useState } from "react"; import { getInfiniteScroll } from "../apis/posts"; const Feed = () => { const [data, setData] = useState([]); const [skip, setSkip] = useState(0); const [isLoading, setIsLoading] = useState(false); // 防止重复请求 useEffect(() => { fetchPosts(); }, [skip]); const fetchPosts = async () => { if (isLoading) return; setIsLoading(true); try { const newPosts = await getInfiniteScroll(skip); setData(prev => [...prev, ...newPosts]); // 追加新数据而非替换 } catch (error) { console.log(error.message); } finally { setIsLoading(false); } }; const handleScroll = (e) => { const { offsetHeight, scrollTop, scrollHeight } = e.target; // 滚动接近底部时触发加载,增加小偏移量避免边界误判 if (offsetHeight + scrollTop >= scrollHeight - 10 && !isLoading) { // 建议用固定每页条数更新skip,比如每页10条,避免闭包导致的旧值问题 setSkip(prev => prev + 10); // 如果接口依赖data.length,可改为setSkip(data.length),但需注意异步更新延迟 } }; return ( <Box flex={4} sx={{ padding: { xs: "0", sm: "0px 20px " }, overflow: "auto", height: "100vh", // 设置固定高度,让容器出现滚动条 maxHeight: "100vh" }} onScroll={handleScroll} > <Post post={data} /> {isLoading && <div>加载中...</div>} {/* 显示加载状态 */} <Box sx={{ display: { sm: "none", xs: "block" }, justifyContent: "center", alignItems: "center", paddingBottom: "50px", }}> <About /> </Box> </Box> ); }; export default Feed;
关键修复说明
- 添加固定高度:给滚动容器设置
height: 100vh,确保内容超过容器高度时出现滚动条,触发onScroll事件。 - 修正数据追加逻辑:使用
setData(prev => [...prev, ...newPosts])保留原有帖子,实现滚动加载的累加效果。 - 移除无效DOM操作:直接使用React的
onScrollprops绑定事件,无需手动操作DOM,避免元素不存在的错误。 - 新增加载状态:防止滚动到底部时重复触发API请求,避免接口压力和数据重复。
- 优化skip更新:使用函数式更新
setSkip(prev => prev + 10),避免闭包导致的旧值问题(若接口依赖data.length,可替换为setSkip(data.length))。
内容的提问来源于stack exchange,提问作者Shiv Shankar Prasad
相关产品推荐
相关产品推荐

