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

如何使用Intersection Observer实现调用API的React无限滚动功能

问题排查与修正方案

现有代码的核心错误点如下:

  • IntersectionObserver对应的useEffect没有设置依赖项、没有配置销毁逻辑,同时直接使用setPage(page + 1)会触发闭包陷阱,拿到的始终是初始的page值,无法正确递增
  • 所有列表项都绑定了同一个lastItem ref,导致ref始终指向遍历的最后一个元素,且没有在元素更新后重新绑定观察逻辑
  • 更新图片列表时直接用新数据替换旧数据,不会保留之前加载的内容
  • 没有加载状态锁,滚动触发时可能同时发起多个重复请求

以下是修正后的完整可运行代码:

import { useState, useRef, useEffect, useCallback } from 'react';
// 保留你原有导入:Nav、Footer、样式文件等

const YourComponent = () => {
  const [page, setPage] = useState(1);
  const [images, setImages] = useState([]);
  // 新增加载状态锁,防止重复触发请求
  const [isLoading, setIsLoading] = useState(false);
  const observer = useRef();

  // 封装最后一个列表元素的观察逻辑,用useCallback避免重复创建
  const lastItemRef = useCallback(node => {
    if (isLoading) return;
    // 先断开之前的观察绑定
    if (observer.current) observer.current.disconnect();

    observer.current = new IntersectionObserver(entries => {
      // 仅当目标元素进入视口时才递增页码
      if (entries[0].isIntersecting) {
        // 用函数式更新避免闭包陷阱
        setPage(prevPage => prevPage + 1);
      }
    });

    if (node) observer.current.observe(node);
  }, [isLoading]);

  // 数据请求逻辑
  useEffect(() => {
    async function fetchData() {
      setIsLoading(true);
      const res = await fetch(`https://api.themoviedb.org/3/trending/movie/week?api_key=${apiKey}&page=${page}`);
      const data = await res.json();
      // 追加新数据而非替换原有数据
      setImages(prev => [...prev, ...data.results]);
      setIsLoading(false);
    }
    fetchData();
  }, [apiKey, page]);

  if (!images.length && isLoading) {
    return <h2>Loading...</h2>;
  }

  return (
    <>
      <Nav />
      <main>
        <section className={styles.spacer}>
          <h1>Trending Movies</h1>
          <ul className={styles.grid}>
            {images.map((image, index) => {
              return (
                <li 
                  className={styles.gridItem} 
                  key={image.id} 
                  // 仅给列表最后一个元素绑定观察ref
                  ref={index === images.length - 1 ? lastItemRef : null}
                >
                  <img 
                    src={`https://image.tmdb.org/t/p/w370_and_h556_bestv2/${image.poster_path}`} 
                    alt={image.original_title} 
                  />
                  <p className={styles.title}>{image.original_title}</p>
                </li>
              );
            })}
          </ul>
          {isLoading && <p>加载更多内容中...</p>}
        </section>
      </main>
      <Footer />
    </>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 10:57:07