You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 16:55:39