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

React+Express滚动加载SoundCloud资源出现重复内容求助

问题排查与修复方案

核心问题根源

  1. React元素key使用错误:用index作为key会导致数组更新时React复用旧组件,引发渲染错乱、内容重复。
  2. Ref数组内存泄漏:每次渲染都向elementsRef中push新元素,导致数组积累大量重复DOM引用,IntersectionObserver重复监听触发多次加载。
  3. 无限滚动Observer重复创建:监听最后一个元素的useEffect依赖usersData,每次数据更新都重建Observer,旧Observer未清理会触发重复请求。
  4. 后端缓存刷新导致分页内容重复:后端定时洗牌缓存数组,前端offset持续累加,相同偏移量对应内容因数组顺序改变而重复。

分步修复

1. 替换React元素的唯一Key

将index替换为track的唯一ID(如data.track.id),确保React正确识别元素变化:

{usersData.map((data, index) => {
  const isLastItem = index === usersData.length - 1;
  return (
    <div
      className="feed-player-container"
      data-user={data.user}
      ref={(element) => {
        elementsRef.current[index] = element;
        if (isLastItem) lastTrackRef.current = element;
      }}
      key={data.track.id} // 使用track唯一ID作为key
    >
      <ReactPlayer url={data.track.url} className="react-player" />
    </div>
  );
})}

2. 修复Ref数组重复添加问题

直接通过索引赋值Ref,避免每次渲染都push重复元素:

ref={(element) => {
  elementsRef.current[index] = element; // 按索引覆盖,防止重复
  if (isLastItem) lastTrackRef.current = element;
}}

3. 优化无限滚动请求逻辑

添加加载状态防止重复请求,重构Observer依赖避免重复创建:

const [isLoading, setIsLoading] = useState(false);

const fetchTracks = () => {
  if (isLoading) return; // 阻止重复请求
  setIsLoading(true);
  fetch(`http://localhost:4000/api/soundcloud?offset=${offset}&limit=${limit}`)
    .then((response) => {
      if (!response.ok) throw new Error(`Request failed: ${response.status}`);
      return response.json();
    })
    .then((data) => {
      setUsersData(old => [...old, ...data.message]);
      setOffset(old => old + limit);
    })
    .catch(console.error)
    .finally(() => setIsLoading(false));
};

// 重构最后元素监听逻辑
useEffect(() => {
  const observer = new IntersectionObserver(
    entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting && !isLoading) fetchTracks();
      });
    },
    { threshold: 0.5 }
  );

  const currentLast = lastTrackRef.current;
  if (currentLast) observer.observe(currentLast);
  
  return () => {
    if (currentLast) observer.unobserve(currentLast);
  };
}, [lastTrackRef.current, isLoading]);

4. 解决后端缓存刷新导致的内容重复

后端添加缓存版本标识,前端检测版本变化时重置数据:

后端修改:

let dataCache = null;
let cacheVersion = 0; // 新增缓存版本号

const fetchData = async () => {
  // ... 原有逻辑 ...
  shuffleArray(arr);
  dataCache = arr;
  cacheVersion += 1; // 缓存刷新时版本号+1
};

app.get("/api/soundcloud", (req, res) => {
  const offset = parseInt(req.query.offset) || 0;
  const limit = parseInt(req.query.limit) || 10;

  if (dataCache) {
    const paginatedData = dataCache.slice(offset, offset + limit);
    res.json({
      message: paginatedData,
      version: cacheVersion // 返回当前版本
    });
  } else {
    res.status(500).json({ error: "No data available" });
  }
});

前端修改:

const [cacheVersion, setCacheVersion] = useState(null);

const fetchTracks = () => {
  if (isLoading) return;
  setIsLoading(true);
  fetch(`http://localhost:4000/api/soundcloud?offset=${offset}&limit=${limit}`)
    .then(response => {
      if (!response.ok) throw new Error(`Request failed: ${response.status}`);
      return response.json();
    })
    .then(data => {
      if (!cacheVersion) {
        setCacheVersion(data.version);
        setUsersData(data.message);
        setOffset(limit);
      } else if (data.version !== cacheVersion) {
        // 缓存已刷新,重置数据
        setUsersData(data.message);
        setOffset(limit);
        setCacheVersion(data.version);
      } else {
        setUsersData(old => [...old, ...data.message]);
        setOffset(old => old + limit);
      }
    })
    .catch(console.error)
    .finally(() => setIsLoading(false));
};

5. 优化电台Logo监听逻辑

清理重复的Observer监听:

useEffect(() => {
  const observer = new IntersectionObserver(
    entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          setCurrentRadioStation(entry.target.dataset.user);
        }
      });
    },
    { threshold: 0.5 }
  );

  elementsRef.current.forEach(element => {
    if (element) observer.observe(element);
  });

  return () => {
    elementsRef.current.forEach(element => {
      if (element) observer.unobserve(element);
    });
  };
}, [usersData]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 03:42:07