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

基于FakeStoreAPI的无限滚动应用:滚动底部数据更新异常排查

无限滚动应用问题分析与修复方案

问题成因

1. 重复加载多项

  • 滚动事件触发时未检查loading状态,滚动到底部的判定逻辑会连续触发多次handleScroll,导致同一时间发起多个重复请求。
  • handleScroll中先调用fetchData再设置loading=true,顺序错误,无法阻止后续重复触发。

2. UI不渲染新数据

  • useEffect依赖为空数组,handleScroll和fetchData捕获的是组件初始化时的page值(始终为1),导致每次请求都获取第一页数据,即使state在累加,也只是重复添加相同内容,且page无法正确递增。
  • 未清理滚动事件监听,可能引发内存泄漏,也会影响组件卸载后的异常事件触发。

修改方案

步骤1:修复闭包与状态捕获问题

用useCallback包裹handleScroll,确保函数能捕获最新的page和loading状态;同时在useEffect中添加对应依赖,保证事件监听能更新到最新的函数。

步骤2:阻止重复请求

在handleScroll中先判断loading状态,只有未加载时才发起请求,且先设置loading=true再调用fetchData,避免重复触发。

步骤3:添加事件清理

在useEffect的返回函数中移除滚动事件监听,避免内存泄漏。

修改后的完整代码

import { useState, useEffect, useCallback } from "react";

const PAGE_NUMBER = 1;

function App() {
  const [state, setState] = useState([]);
  const [page, setPage] = useState(PAGE_NUMBER);
  const [loading, setLoading] = useState(false);

  const fetchData = async () => {
    try {
      const res = await fetch(
        `https://fakestoreapi.com/products?limit=5&page=${page}`
      );
      const data = await res.json();
      setState((prevData) => [...prevData, ...data]);
      setPage((prevPage) => prevPage + 1);
    } catch (err) {
      console.error('数据加载失败:', err);
    } finally {
      setLoading(false);
    }
  };

  const handleScroll = useCallback((e) => {
    if (loading) return;
    if (
      window.innerHeight + e.target.documentElement.scrollTop + 1 >=
      e.target.documentElement.offsetHeight
    ) {
      setLoading(true);
      fetchData();
    }
  }, [loading, page]);

  useEffect(() => {
    fetchData();
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [handleScroll]);

  return (
    <div className="App">
      <h1>无限滚动示例</h1>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '1rem' }}>
        {state.map((product) => (
          <div key={product.id} style={{ border: '1px solid #eee', padding: '1rem', borderRadius: '8px' }}>
            <img src={product.image} alt={product.title} style={{ width: '100%', height: '150px', objectFit: 'contain' }} />
            <h3 style={{ fontSize: '0.9rem', marginTop: '0.5rem' }}>{product.title}</h3>
          </div>
        ))}
      </div>
      {loading && <p style={{ textAlign: 'center', padding: '1rem' }}>加载中...</p>}
    </div>
  );
}

export default App;

额外优化点

  • 增加了请求错误捕获,避免请求失败导致loading一直处于true状态。
  • 添加了加载状态提示,提升用户体验。
  • 优化了列表项布局样式,让展示更规整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 09:01:22