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

React setState未保留历史状态:无限滚动组件items数组重置为空

无限滚动组件items重置为空数组的排查与修复

问题分析

你的无限滚动组件中,items数组在加载新数据时丢失旧数据(表现为重置为空),核心原因是闭包捕获旧状态和useEffect依赖项设置错误:

  • fetchData函数中直接使用的items是组件初始渲染时的空数组,由于useEffect依赖未正确设置,fetchData始终是初始版本,每次调用都会用空数组合并新数据,导致旧数据被覆盖。
  • useEffect将observerTarget作为依赖,但useRef返回的ref对象本身是稳定的,不会触发useEffect重新执行,使得fetchData永远捕获初始的空items状态。

修复步骤

1. 改用函数式更新状态

将setItems改为函数式调用,直接获取最新的items状态,彻底避免闭包问题:

// 原代码
setItems([...items, ...data.data]);
// 修改后
setItems(prevItems => [...prevItems, ...data.data]);

2. 用useCallback包裹fetchData并修正依赖

用useCallback包裹fetchData,确保当request变化时生成新的函数版本,同时将fetchData和loading加入useEffect依赖,保证回调能获取最新状态:

const fetchData = useCallback(async () => {
  setLoading(true);
  try {
    const res = await fetch(request.url, request.options);
    if (res.ok) {
      const data = await res.json();        
      setItems(prevItems => [...prevItems, ...data.data]);
      setError(null); // 请求成功时清空错误状态
    } else {
      setError(new Error(`请求失败,状态码:${res.status}`));
    }
  } catch (err) {
    console.log(err);
    setError(err);
  } finally {
    setLoading(false);
  }
}, [request]);

3. 防止重复请求

在IntersectionObserver回调中加入!loading判断,避免数据加载过程中重复触发请求:

if (entries[0].isIntersecting && !loading) {
  console.log("isIntersecting, fetching new data...");
  fetchData();
}

完整修复代码

"use client";
import { useState, useEffect, useRef, useCallback } from "react";

export function HBSInfiniteScrollList({ request = {} }) {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const observerTarget = useRef(null);

  const fetchData = useCallback(async () => {
    setLoading(true);
    try {
      const res = await fetch(request.url, request.options);
      if (res.ok) {
        const data = await res.json();        
        setItems(prevItems => [...prevItems, ...data.data]);
        setError(null);
      } else {
        setError(new Error(`请求失败,状态码:${res.status}`));
      }
    } catch (err) {
      console.log(err);
      setError(err);
    } finally {
      setLoading(false);
    }
  }, [request]);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting && !loading) {
          console.log("isIntersecting, fetching new data...");
          fetchData();
        }
      },
      { threshold: 1 }
    );

    const target = observerTarget.current;
    if (target) {
      observer.observe(target);
    }

    return () => {
      if (target) {
        observer.unobserve(target);
      }
    };
  }, [fetchData, loading]);

  return (
    <div>
      <ul className="list-group">
        {items.map((item) => (
          <li key={item.id} className="list-group-item">
            {item.name}
          </li>
        ))}
      </ul>
      {loading && <p>Loading...</p>}
      {error && <p>Error: {error.message}</p>}
      <p>length: {items.length}</p>
      <div ref={observerTarget}></div>
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:13:12