React中Infinite Scrolling拉取数据失效导致运行卡顿问题
React无限滚动新闻列表卡死问题修复
核心卡死原因
你遇到的电脑卡死本质是请求死循环+内存爆炸,由几个写法错误共同导致:
- 直接修改state:
fetchMoreData里写this.state.page += this.state.page违反React类组件的状态更新规则,既不会触发正常的重渲染,还会让页码直接翻倍(1→2→4→8→16...指数级跳页) - 无限滚动组件判断
hasMore条件始终成立,又找不到你配置的scrollableTarget="scrollableDiv"对应的DOM节点,会不停触发fetchMoreData回调,短时间内发出上百个接口请求,返回的大量数据塞满内存直接导致页面崩溃 - 没有加载锁,上一个请求没返回的时候下一个请求已经发出去,进一步放大请求并发量
- 列表项用数组下标做key,追加数据时React重复渲染DOM节点,越加载越卡
- 错误开启
inverse={true}配置,这个参数是给聊天框那种滚到顶部加载历史的场景用的,普通向下滚动的新闻列表开这个会导致滚动判断逻辑完全错乱
修复步骤
- 所有state修改必须走
setState,页码更新逻辑改为每次+1,禁止直接赋值this.state.xxx - 修正InfiniteScroll配置:关闭inverse,删掉不存在的scrollableTarget配置,默认绑定window滚动即可
- 给加载逻辑加锁,请求过程中禁止重复触发下一页拉取
- 列表项key改用新闻条目的唯一url字段,不要用数组下标
- 补全你代码里没写完的render闭合标签,避免编译异常
修复后的关键代码
修正fetchMoreData方法
async fetchMoreData() { // 加载中直接返回,防止重复触发 if (this.state.loading) return; // 用setState的函数式更新拿最新页码,更新完再发请求 this.setState( prev => ({ page: prev.page + 1, loading: true }), async () => { try { const url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=01b1a8fe6ec74a02b41f4e0050d4d394&page=${this.state.page}&pageSize=${this.props.pageSize}`; const res = await fetch(url); const parsedData = await res.json(); this.setState(prev => ({ articles: prev.articles.concat(parsedData.articles || []), loading: false, totalResults: parsedData.totalResults })) } catch (err) { console.error("加载更多失败", err); this.setState({ loading: false }) } } ) }
修正首次加载updateNews方法
async updateNews() { try { // 切换分类/首次加载时重置状态 this.setState({ loading: true, page: 1, articles: [], totalResults: 0 }) const url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=01b1a8fe6ec74a02b41f4e0050d4d394&page=1&pageSize=${this.props.pageSize}`; const data = await fetch(url); const parsedData = await data.json(); this.setState({ articles: parsedData.articles || [], loading: false, totalResults: parsedData.totalResults }) } catch (e) { console.log("新闻加载失败"); this.setState({ loading: false }) } }
修正InfiniteScroll组件配置
<InfiniteScroll dataLength={this.state.articles.length} next={this.fetchMoreData} inverse={false} hasMore={this.state.articles.length < this.state.totalResults} loader={<Spinner />} > <div className="container my-4"> <div className="row"> {this.state.articles.map((element) => { return ( <div className="col-md-4 my-4" key={element.url}> <NewsItem title={element.title ? element.title.slice(0, 40) : ""} description={element.description ? element.description.slice(0, 40) : ""} urlToImage={element.urlToImage} author={element.author} date={element.publishedAt} url={element.url} source={element.source.name} /> </div> ) })} </div> </div> </InfiniteScroll>
验证方式
改完后打开浏览器开发者工具的网络面板,正常逻辑是只有滚动到列表底部时才会发起一次下一页请求,不会出现连续批量发请求的情况,列表滚动流畅不会卡顿。如果还有频繁触发的问题,可以给fetchMoreData加300ms节流即可。
内容的提问来源于stack exchange,提问作者Usama Javed
相关产品推荐
相关产品推荐

