阅读 232

ReactorKit + RxDataSources 列表多次刷新的解决方案

相信使用 ReactorKit + RxDataSources 的同学都有遇到列表会多次刷新的问题吧,本篇将提出我的解决方案,相互学习交流

一、常规使用

Reactor

enum Mutation {     case setSections([LXFSection])     ... } struct State {     var sections : [LXFSection] = []     ... } 复制代码

View

reactor.state.map { $0.sections }     .bind(to: tableView.rx.items(dataSource: dataSource))     .disposed(by: disposeBag) 复制代码

由于在 ReactorKit 中,View 对状态的订阅都是针对 State 来说的,而非 State 中的属性,所以只要 State 的值发生改变,View 中所有对 State 的订阅回调都会被调用,从而使视图进行更新。

但是一个属性的变化,理应只需要更新对应的视图即可,要达到这一效果,就需要使用 distinctUntilChanged 这个方法,对于 State 中遵守了 Equatable 协议的属性,直接加上 .distinctUntilChanged() 即可

但是看我们定义的 Section

import RxDataSources enum LXFSection {     case list([LXFSectionItem]) } extension LXFSection: SectionModelType {     init(original: LXFSection, items: [LXFSectionItem]) {         switch original {         case .list: self = .list(items)         }     }          var items: [LXFSectionItem] {         switch self {         case .list(let items): return items         }     } } enum LXFSectionItem {     case item(LXFCellReactor) } 复制代码

需要自己给 LXFSection 遵守 Equatable 并实现协议方法,或者是在 distinctUntilChanged 回调中自己做判断 ,但都太麻烦了~

reactor.state.map { $0.sections }     .distinctUntilChanged({ (sectionArr1: [LXFSection], sectionArr2: [LXFSection]) in         return 判断 sectionArr1 和 sectionArr2 是否相等     })     .bind(to: tableView.rx.items(dataSource: dataSource))     .disposed(by: disposeBag) 复制代码

那有没有其它办法呢?答案当然是有的

二、优化

创建一个名为 SwiftyTraceableValue 的结构体,定义如下:

public struct SwiftyTraceableValue<T> {     public var tracker: Int = 0     public var value: T } 复制代码

内部属性说明:

属性作用
tracker用于判断是否数据是否有发生变化
value存储原来的值

调整 Mutation

enum Mutation {     case setSections(TraceableValue<[LXFSection]>)     ... } 复制代码

调整 State

struct State {     var sections : TraceableValue<[LXFSection]> = .init(value: [])     ... } 复制代码

调整 Sections 数据的处理

let oldTracker = self?.currentState.sections.tracker ?? 0 let oldSections = self?.currentState.sections.value ?? [] items = oldSections.first?.items ?? [] + items let finalSections = [LXFSection.list(items)] let sections = TraceableValue(tracker: oldTracker + 1, value: finalSections) let setSections = Observable.just(Mutation.setSections(sections)) 复制代码

调整 View

reactor.state.map { $0.sections } .distinctUntilChanged {         $0.tracker == $1.tracker     }     .map { $0.value }     .bind(to: tableView.rx.items(dataSource: dataSource))     .disposed(by: disposeBag) 复制代码

至此,按如上的内容进行调整便可以解决列表多次刷新的问题,但是这改动也忒大了,而且还要自己管理 tracker 的值,这怎么能忍?

那有什么办法可以让我们不再手动管理 tracker 呢?????

这里我们可以使用 SwiftProperty Wrapper????

三、改进

我们将 SwiftyTraceableValue 使用 Property Wrapper 进行封装

@propertyWrapper public struct SwiftyTraceableValue<T> {     public  var tracker: Int = 0     public var value: T          public var projectedValue: SwiftyTraceableValue {         return self     }          public var wrappedValue: T {         get {             return self.value         } set {             self.tracker += 1             self.value = newValue         }     }          public init(wrappedValue: T) {         self.value = wrappedValue     } } 复制代码

这样,每次被赋值时即可自动为 tracker1。这里 projectedValue 我们返回真实类型 SwiftyTraceableValue ,以便后续使用。

这里简单提一下属性包装器的使用,更加具体详细的内容可以看我另一篇文章《Swift - PropertyWrapper》

// 使用 @SwiftyTraceableValue 对属性进行修饰 @SwiftyTraceableValue var sections : [LXFSection] = [] // 获取 wrappedValue 的值 sections // 获取 projectedValue 的值 $sections 复制代码

不仅如此,我们还为 SwiftyTraceableValue 专门扩展 ObservableType,将进行比较内容部分封装起来

public extension ObservableType {     func mapDistinctUntilTraceableValueChanged<T>(         _ transform: @escaping (Element) throws -> SwiftyTraceableValue<T>     ) -> Observable<T> {         return self             .map(transform)             .distinctUntilChanged { $0.tracker == $1.tracker }             .map { $0.value }     } } 复制代码

大功造成,接下来看看如何使用吧

四、使用

以下内容是基于第一部分进行调整的,大家可以忘掉第二部分的修改了

最终我们只需要调整两处地方即可解决列表多次刷新的问题!

Reactor

struct State {     @SwiftyTraceableValue var sections : [LXFSection] = []     ... } 复制代码

View

reactor.state.mapDistinctUntilTraceableValueChanged { $0.$sections }     .bind(to: tableView.rx.items(dataSource: dataSource))     .disposed(by: disposeBag) 复制代码

现在,我将它做成了开源库,方便大家使用,好用的话请 Star 吧 ????


作者:LinXunFeng
链接:https://juejin.cn/post/7039367289170296863

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐