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> )) )} </> ); };
关键修改说明
- 提交搜索时清空旧数据:在
onSubmit方法中新增setMovies([]),保证新搜索的结果不会和旧结果拼接,同时也在useEffect中增加了page=1时直接替换数组的双重保险 - 修正交叉监听绑定逻辑:只把
myRef绑定到电影列表的最后一个元素上,避免每一个卡片都触发监听导致分页异常 - 修复空数据判断逻辑:新增
fetchError状态存储接口返回的错误信息,当没有数据时正常展示提示 - 优化错误捕获逻辑:只处理非取消请求的报错,避免切换搜索时的取消请求被当做异常处理
内容的提问来源于stack exchange,提问作者Muhammad Haekal
相关产品推荐
相关产品推荐

