I'm trying to get my head around ViewStores and how to correctly pare things down to minimise unnecessary redraws…
e.g.
Trying to add pull-to-fresh to a List—currently using SwiftUIRefresh
.
The following code triggers a redraw of the whole list on every isRefreshing
change. I think, at least in this trivial case, I need something more like the WithViewStore(store)
to be stateless
, and to be able to rescope the Store
to a new ViewStore
on pullToRefresh
…
I'm sure I must be missing something… any tips? Cheers. :)
struct AppState: Equatable {
var states: [Int] = [0]
var isRefreshing = false
}
enum AppAction: Equatable {
case refreshRequested
case refreshing(Bool)
case states(index: Int, action: Never)
}
let reducer = Reducer<AppState, AppAction, Void> { state, action, _ in
switch action {
case .refreshRequested:
// Terminate after 1 second, no updates
return Effect(value: .refreshing(false))
.delay(for: 1, scheduler: DispatchQueue.main.eraseToAnyScheduler())
.eraseToEffect()
case let .refreshing(new):
state.isRefreshing = new
return .none
}
}
struct ContentView: View {
let store: Store<AppState, AppAction> = Store(initialState: AppState(), reducer: reducer, environment: ())
var body: some View {
WithViewStore(store) { viewStore in
List {
ForEachStore(
store.scope(state: \.states, action: AppAction.states),
id: \.self,
content: row
)
}
.pullToRefresh(viewStore.binding(get: \.isRefreshing, send: AppAction.refreshing)) {
viewStore.send(.refreshRequested)
}
}
}
func row(store: Store<Int, Never>) -> some View {
print("Rendering row")
return WithViewStore(store) { viewStore in
Text("\(viewStore.state)")
}
}
}