ReactJS基于JSON数据实现无限滚动遇innerHeight未定义,求无第三方库方案
React原生无限滚动实现方案(修复innerHeight未定义问题)
问题根源
你代码里的innerHeight报错是因为DOM元素没有innerHeight属性,这个属性属于window对象。对于DOM元素,应该使用clientHeight来获取可视区域高度。
修正后的完整实现代码
import { useState, useEffect, useRef, useCallback } from 'react'; function App() { const [response, setResponse] = useState([]); const [currentPage, setCurrentPage] = useState(1); const [isLoading, setIsLoading] = useState(false); const scrollContainerRef = useRef(null); // 每页加载数量 const PAGE_SIZE = 10; // 存储全部数据,避免重复请求 const allDataRef = useRef([]); // 初始化加载全部数据(实际项目中建议用分页接口,这里用jsonplaceholder模拟) useEffect(() => { fetch('https://jsonplaceholder.typicode.com/comments') .then(res => res.json()) .then(data => { allDataRef.current = data; // 加载第一页数据 const initialData = data.slice(0, PAGE_SIZE); setResponse(initialData); }); }, []); // 加载更多数据的逻辑 const loadMoreData = useCallback(() => { if (isLoading) return; setIsLoading(true); const nextStart = currentPage * PAGE_SIZE; const nextEnd = nextStart + PAGE_SIZE; const newData = allDataRef.current.slice(nextStart, nextEnd); if (newData.length > 0) { setResponse(prev => [...prev, ...newData]); setCurrentPage(prev => prev + 1); } setIsLoading(false); }, [currentPage, isLoading]); // 滚动触发逻辑 const handleInfiniteScroll = useCallback(() => { const container = scrollContainerRef.current; if (!container) return; // 滚动触发条件:滚动距离 + 可视高度 >= 容器总高度 - 100(提前加载的阈值) const shouldLoadMore = (container.scrollTop + container.clientHeight) >= (container.scrollHeight - 100); if (shouldLoadMore) { loadMoreData(); } }, [loadMoreData]); // 绑定滚动事件(也可以直接在元素上用onScroll) useEffect(() => { const container = scrollContainerRef.current; if (container) { container.addEventListener('scroll', handleInfiniteScroll); return () => container.removeEventListener('scroll', handleInfiniteScroll); } }, [handleInfiniteScroll]); return ( <div className='container'> <div className='box' ref={scrollContainerRef} style={{ height: '600px', overflowY: 'auto', border: '1px solid #eee', padding: '10px' }} > {response.map((item) => ( <div key={item.id} style={{ padding: '8px 0', borderBottom: '1px solid #f0f0f0' }}> {item.name} </div> ))} {isLoading && <div style={{ textAlign: 'center', padding: '10px' }}>加载中...</div>} {response.length === allDataRef.current.length && ( <div style={{ textAlign: 'center', padding: '10px', color: '#999' }}>已加载全部数据</div> )} </div> </div> ); } export default App;
关键优化点说明
- 属性修正:用
clientHeight替代innerHeight,正确获取容器可视高度 - 避免重复请求:用
isLoading状态防止滚动时重复触发加载 - 数据处理优化:用
allDataRef存储全部数据,避免重新请求,通过slice截取分页数据(原代码的splice会直接修改原数组,容易导致数据丢失) - 提前加载阈值:设置100px的提前加载距离,提升用户体验
- 事件绑定优化:用
useCallback缓存函数,配合useEffect绑定/解绑滚动事件,避免不必要的重渲染 - 状态提示:添加加载中和加载完成的状态提示
内容的提问来源于stack exchange,提问作者devReact
相关产品推荐
相关产品推荐

