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

使用RxSwift实现下拉刷新与加载更多时无限触发请求的问题

解决RxSwift下拉刷新/加载更多无限触发问题

你遇到的核心问题是缺少“无更多数据”的状态控制:当下一页请求返回空数据时,你的代码没有标记“已加载完全部数据”,再加上滚动到底部的事件会持续触发,就导致了loadNextPageTrigger被无限调用的情况。下面是具体的分析和修复方案:

问题根源拆解

  1. 没有标记“是否还有更多数据”的状态变量,只要滚动到底部就会尝试发起请求
  2. 每次触发loadNextPageTrigger时,不管当前是否还有数据,都会递增pageIndex并发起请求
  3. 空数据返回后没有终止后续加载的逻辑,导致循环触发

修复方案

我们需要添加状态控制,同时优化请求触发的条件,具体步骤如下:

1. 新增状态变量

在MomentViewModel中添加hasMoreData变量,用来标记是否还有更多数据可以加载:

private let hasMoreData = Variable<Bool>(true)

2. 优化请求触发条件

修改refreshRequest和nextPageRequest的触发逻辑,加入hasMoreData的校验:

  • 刷新时重置hasMoreData为true(保证下拉刷新后可以重新加载分页数据)
  • 下一页请求只有在未加载中且还有更多数据时才允许发起

3. 根据响应更新状态

请求返回数据后,根据返回结果判断是否还有更多数据(优先使用接口返回的分页标记,比如isLastPage;如果没有则通过返回数据的数量判断)

完整修改后的ViewModel代码

public final class MomentViewModel { 
    // Property 
    let refreshTrigger = PublishSubject<Void>() 
    let loadNextPageTrigger = PublishSubject<Void>() 
    let loading = Variable<Bool>(false) 
    let posts = Variable<[Post]>([]) 
    var pageIndex: Int = 0 
    let error = PublishSubject<Swift.Error>() 
    // 新增:标记是否还有更多数据可加载
    private let hasMoreData = Variable<Bool>(true)
    private let disposeBag = DisposeBag() 

    public init() { 
        let refreshRequest = Observable.combineLatest(loading.asObservable(), hasMoreData.asObservable())
            .sample(refreshTrigger)
            .flatMap { [unowned self] (loading, _) -> Observable<Int> in 
                if loading { 
                    return Observable.empty() 
                } else { 
                    return Observable<Int>.create { observer in 
                        self.pageIndex = 0 
                        // 刷新时重置"有更多数据"状态
                        self.hasMoreData.value = true
                        print("reset page index to 0") 
                        observer.onNext(0) 
                        observer.onCompleted() 
                        return Disposables.create() 
                    } 
                } 
            } 
            .debug("refreshRequest", trimOutput: false) 

        let nextPageRequest = Observable.combineLatest(loading.asObservable(), hasMoreData.asObservable())
            .sample(loadNextPageTrigger)
            .flatMap { [unowned self] (loading, hasMore) -> Observable<Int> in 
                // 只有未加载且还有更多数据时,才发起下一页请求
                if loading || !hasMore { 
                    return Observable.empty() 
                } else { 
                    return Observable<Int>.create { [unowned self] observer in 
                        self.pageIndex += 1 
                        print(self.pageIndex) 
                        observer.onNext(self.pageIndex) 
                        observer.onCompleted() 
                        return Disposables.create() 
                    } 
                } 
            } 
            .debug("nextPageRequest", trimOutput: false) 

        let request = Observable.merge(refreshRequest, nextPageRequest) 
            .debug("Request", trimOutput: false) 

        let response = request.flatMapLatest { page in 
            RxAPIProvider.shared.getPostList(page: page).materialize() 
        } 
        .share(replay: 1)
        
        // 处理成功响应,更新列表数据
        response.elements()
            .debug("Response", trimOutput: false)
            .withLatestFrom(posts.asObservable()) { (newPosts, oldPosts) -> [Post] in
                return self.pageIndex == 0 ? newPosts : oldPosts + newPosts
            }
            .bind(to: posts)
            .disposed(by: disposeBag)
        
        // 根据返回数据更新"是否还有更多数据"的状态
        // 这里假设每页预期返回10条数据,可根据你的实际接口规则调整
        response.elements()
            .subscribe(onNext: { [unowned self] newPosts in
                // 如果返回空数据,或者数据量小于每页预期,标记为无更多数据
                self.hasMoreData.value = !newPosts.isEmpty && newPosts.count >= 10
                // 如果你接口返回了明确的分页标记(比如isLastPage),建议用这个字段判断:
                // self.hasMoreData.value = !response.isLastPage
            })
            .disposed(by: disposeBag)
        
        // 处理错误响应
        response.errors()
            .bind(to: error)
            .disposed(by: disposeBag)

        // 更新加载状态
        Observable 
            .merge(request.map{ _ in true }, 
                   response.map { _ in false }, 
                   error.map { _ in false }) 
            .bind(to: loading) 
            .disposed(by: disposeBag) 
    } 
}

关键优化点说明

  • 新增hasMoreData状态,从根源上阻止无意义的下一页请求
  • 刷新时强制重置hasMoreData,保证每次下拉刷新都能重新加载分页数据
  • 拆分响应处理逻辑,让更新列表、状态判断、错误处理的职责更清晰
  • 优先推荐使用接口返回的分页标记(如isLastPage)来判断是否还有更多数据,比通过数据数量判断更可靠

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:00:37