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

ReactJS无第三方库实现无限滚动 新搜索结果未覆盖旧内容问题

问题原因

你的无限滚动搜索结果累加的核心问题是:触发新搜索时没有清空之前存储的旧电影列表数据,新搜索返回的第一页结果仍然被拼接在旧数组尾部,所以会出现新旧搜索结果同时展示的情况。

同时你的代码还有两处可优化的逻辑问题:

  • 把监听滚动的ref绑定到了所有电影卡片上,会导致交叉监听重复触发,分页逻辑异常
  • 现有movies === "False"的判断永远不会生效,因为movies是数组类型,不可能等于字符串值

修复后的完整代码

import React, { useEffect, useState, useRef, useCallback } from "react";
import axios from "axios";

const my_key = process.env.REACT_APP_MY_KEY;

export const InfiniteScroll = () => {
  const [movies, setMovies] = useState([]);
  const [movieName, setMovieName] = useState("ant man");
  const [page, setPage] = useState(1);
  const [search, setSearch] = useState("");
  const [hasMore, setHasMore] = useState(false);
  // 新增错误状态用于提示无结果
  const [fetchError, setFetchError] = useState("");

  useEffect(() => {
     let cancel;
     setFetchError("");
     axios({
        method: "GET",
        url: "http://www.omdbapi.com/",
        params: { apikey: `${my_key}`, s: movieName, page: page },
        cancelToken: new axios.CancelToken((c) => (cancel = c)),
     })
     .then(({ data }) => {
        if (data.Response === "True") {
           // 如果是第一页直接替换数组,不是第一页再拼接
           setMovies((prev) => page === 1 ? [...data.Search] : [...prev, ...data.Search]);
           setHasMore(data.Search.length > 0);
        } else {
           setHasMore(false);
           setFetchError(data.Error || "暂无数据");
        }
     })
     .catch((err) => {
        if (!axios.isCancel(err)) setFetchError("请求失败,请重试");
     });
    return () => cancel();
   }, [movieName, page]);

 const myObserver = useRef();
 const myRef = useCallback(
    (node) => {
      if (myObserver.current) myObserver.current.disconnect();
      myObserver.current = new IntersectionObserver((entries) => {
         if (entries[0].isIntersecting && hasMore) {
            setPage((prev) => prev + 1);
         }
      });
      if (node) myObserver.current.observe(node);
      },
     [hasMore]
    );

 const onSubmit = (e) => {
    e.preventDefault();
    setMovieName(search);
    setSearch("");
    setPage(1);
    // 提交新搜索时清空旧数据
    setMovies([]);
    setFetchError("");
  };

 return (
   <>
     <h1>搜索电影</h1>
     <form onSubmit={onSubmit}>
       <input
          type="text"
          placeholder="电影名称"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
       />
       <input
          type="submit"
          value="提交"
          disabled={search === ""}
        />
     </form>
     {fetchError && movies.length === 0 ? (
       <div>
         <h1>{fetchError}</h1>
       </div>
      ) : (
       movies.map(({ imdbID, Title, Year, Poster }, i) => (
         <div key={`${imdbID}-${i}`} ref={i === movies.length -1 ? myRef : null}>
            <img src={Poster} alt={Title} />
            <h2>{Title}</h2>
           <h3>{Year}</h3>
        </div>
      ))
    )}
     </>
   );
};

关键修改说明

  1. 提交搜索时清空旧数据:在onSubmit方法中新增setMovies([]),保证新搜索的结果不会和旧结果拼接,同时也在useEffect中增加了page=1时直接替换数组的双重保险
  2. 修正交叉监听绑定逻辑:只把myRef绑定到电影列表的最后一个元素上,避免每一个卡片都触发监听导致分页异常
  3. 修复空数据判断逻辑:新增fetchError状态存储接口返回的错误信息,当没有数据时正常展示提示
  4. 优化错误捕获逻辑:只处理非取消请求的报错,避免切换搜索时的取消请求被当做异常处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 04:36:03