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

React JS对接Reddit实现无限滚动异常加载问题修复

问题根因

你的代码存在5个核心问题,才会导致无限加载/滚动无响应:

  • 触发逻辑死循环:把数据请求放在依赖page的useEffect中,请求完成后立刻更新page值,会直接触发下一次请求,完全不需要滚动操作,造成无限加载
  • 滚动触发逻辑无效:scrollToEnd方法中写setPage(page)是无意义操作,状态值没有变化不会触发更新,滚到底自然不会加载
  • 无加载锁:没有判断请求状态,滚动事件多次触发时会重复发起并发请求
  • 滚动判断兼容性差:用===严格判断是否滚动到最底部,浏览器缩放、滚动精度、滚动条宽度差异都会导致条件永远不满足
  • 事件绑定不规范:直接重写window.onscroll,每次组件重渲染都会重复绑定事件,组件卸载时也没有清理,会造成内存泄漏;同时更新列表时直接取外层items变量,容易触发React闭包陷阱导致数据覆盖。
修正后完整代码
import React, {useState, useEffect, useCallback} from "react";
import logo from './logo.svg';
import './App.css';
import './Table.css';
import axios from "axios";

function App() {
  const [items, setItems] = useState([]);
  // 存下一页请求需要的after参数,初始为空字符串对应第一页
  const [nextPage, setNextPage] = useState("");
  // 加载锁,防止重复请求
  const [loading, setLoading] = useState(false);
  // 标记是否还有更多数据
  const [hasMore, setHasMore] = useState(true);

  // 抽离数据加载逻辑
  const fetchMore = useCallback(() => {
    // 正在加载、或没有更多数据时直接返回
    if (loading || !hasMore) return;
    setLoading(true);
    axios.get(`https://www.reddit.com/r/aww.json?after=${nextPage}`)
      .then(res=> res.data)           
      .then(data => {
        // 函数式更新,避免闭包拿到旧的items值
        setItems(prev => [...prev, ...data.data.children]);
        const newAfter = data.data.after;
        setNextPage(newAfter);
        // 返回的after为null代表已经到最后一页
        if (!newAfter) setHasMore(false);
      })
      .catch(err => console.error("加载失败:", err))
      .finally(() => setLoading(false));
  }, [loading, hasMore, nextPage]);

  // 组件挂载时自动加载第一页
  useEffect(() => {
    fetchMore();
  }, []);

  // 绑定滚动监听
  useEffect(() => {
    const handleScroll = () => {
      // 距离底部100px时就触发加载,兼容性更好,体验更流畅
      const reachBottom = window.innerHeight + document.documentElement.scrollTop 
        >= document.documentElement.offsetHeight - 100;
      if (reachBottom) fetchMore();
    };
    window.addEventListener('scroll', handleScroll);
    // 组件卸载时移除事件监听,防止内存泄漏
    return () => window.removeEventListener('scroll', handleScroll);
  }, [fetchMore]);

  return (
    <div className="App">
        <h1>Displaying data from Reddit API</h1>
        <table>
            <thead>
            <tr>
                <th>ID</th>
                <th>Photo</th>
                <th>Content</th>
            </tr>
            </thead>
            <tbody>
                {items.map((el) => 
                    <tr key={el.data.id} id={el.data.id}>
                        <td>{el.data.id}</td>
                        <td>
                          <img 
                            src={el.data.thumbnail} 
                            height={el.data.thumbnail_height} 
                            width={el.data.thumbnail_width}
                            alt={el.data.title}
                          />
                        </td>
                        <td>{el.data.title}</td>
                    </tr>
                )}
                {loading && (
                  <tr>
                    <td colSpan={3} style={{textAlign: 'center', padding: '1rem'}}>
                      加载中...
                    </td>
                  </tr>
                )}
                {!hasMore && !loading && (
                  <tr>
                    <td colSpan={3} style={{textAlign: 'center', padding: '1rem'}}>
                      已经到底啦
                    </td>
                  </tr>
                )}
            </tbody>
        </table>
    </div>
  );      
}

export default App;
关键实现规范
  • 加载锁机制:通过loading状态标记请求状态,请求发起前上锁、请求结束后解锁,从根源避免重复并发请求
  • 状态更新规范:列表拼接使用函数式setState,始终基于最新的前序状态更新,避免React闭包陷阱导致的数据丢失/重复
  • 事件绑定规范:滚动事件在useEffect中绑定,通过return的清理函数在组件卸载时移除,避免内存泄漏;不要直接重写window.onscroll属性,用addEventListener绑定更安全
  • 边界兼容处理:
    • 滚动触发留100px提前量,不用严格相等判断,兼容不同设备、浏览器的滚动精度差异
    • 新增hasMore状态,接口返回无下一页标识时停止触发请求,减少无效请求
    • 列表渲染补充key属性、图片补充alt属性,符合React开发规范,消除控制台警告
  • 逻辑解耦:把加载逻辑从依赖page的useEffect中抽离,首次加载、滚动加载都复用同一个fetchMore方法,逻辑更清晰,不会出现状态变化自动触发请求的死循环问题。

内容的提问来源于stack exchange,提问作者Indication

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 22:27:20