Open utterances-bot opened 2 years ago
EmptyView().onAppear() 好像并没有执行,请问这是SwiftUI的问题么?
@yongyang007 啊,我疏忽了,EmptyView
的 modifier 的话确实是什么都不做的。我修改一下...
puulback -> pullback
如果大组件中包含小组件的数组,这种情况下大组件的 reduce 是不是无法用 pullback 拉回小组件的 state 呢?需要怎么样同步小组件的 state 呢?
@RayJiang16 这方面也许在下一篇文章里有一些提及?https://onevcat.com/2022/05/tca-4/
赞!请问有办法对 ViewStore.send(action) 进行类似 Throttle 的操作吗?比如一个 TextField 输入的 Text 可能会非常频繁地 sendAction,但我只想每 0.3 秒获取一次输入的信息发起网络请求。
@Khala-wan 我的想法是直接在 UI 层做一个 PasstgroughSubject
之类的东西,然后直接用 Combine 的 throttle
应该就行了吧。
确实是个解决方案,但是就能不能用 TextField(text: viewStore.binding(\searchText, send: .searchTextChanged)) 这种方式了。需要走从 UI 层走一遍数据转换, 看起来可能有点繁琐。这里有 https://forums.swift.org/t/issues-with-throttling-and-potential-solutions/36812 (另一种更 TCA 的解法)。不过这个方式我在iOS 13 上中了 Combine 的 BadAccess。
@onevcat 我跑了一下demo代码,发现每点击一次+号,CounterView body会调用两次,我理解是子组件state变更刷新了一次,父组件state变更又刷新了一次,这里会有问题,如果嵌套层级很多,很多子组件会刷新很多次,这里是否考虑使用class代替struct不在子组件和父组件之间传递Action和State会解决刷新问题?
@wangfangshuai 这是有可能的:ViewStore
就是让当前 view 订阅某个 store,“默认”情况下,这个 store 里的 state 的所有属性都会被订阅,于是造成性能问题。最近版本的 TCA 为创建 ViewStore
时提供了额外的 observe
参数,来限定父 view 需要订阅的属性。具体的建议参考一下 Performance 的相关文档 以及这里的讨论。
这里是否考虑使用class代替struct不在子组件和父组件之间传递Action和State会解决刷新问题?
完全不推荐这种做法,而且可能这么做会让整个 Publisher 链挂掉。对于 State
角色,尽量避免使用引用语义。
@onevcat 我尝试根据 Performance 的相关文档 的说明进行修改:
struct GameView: View {
let store: Store<GameState, GameAction>
struct ViewState: Equatable {}
var body: some View {
WithViewStore(store.stateless, observe: ViewState.init) { viewStore in
VStack {
TimerLabelView(store: store.scope(state: \.timer, action: GameAction.timer))
CounterView(store: store.scope(state: \.counter, action: GameAction.counter))
}.onAppear {
viewStore.send(.timer(.start))
}
}
}
}
添加observe,初始化一个空的ViewState传入,但是依然会调用两次CounterView的body,你能修改一下demo,让CounterView的body可以调用一次吗?谢谢! Performance 的相关文档的文档并不太容易理解。
你是用什么方式来确认 body 的调用的?我这边尝试了一下,Button 似乎只会触发一次 body,应该是按预期工作的。
struct CounterView: View {
let store: Store<Counter, CounterAction>
var body: some View {
WithViewStore(store) { viewStore in
VStack {
checkLabel(with: viewStore.checkResult)
HStack {
Button("-") { viewStore.send(.decrement) }
TextField(
viewStore.countString,
text: viewStore.binding(
get: \.countString,
send: CounterAction.setCount
)
)
.frame(width: 40)
.multilineTextAlignment(.center)
.foregroundColor(colorOfCount(viewStore.count))
Button("+") { viewStore.send(.increment) }
}
Slider(value: viewStore.binding(get: \.countFloat, send: CounterAction.slidingCount), in: -100...100)
Button("Next") { viewStore.send(.playNext) }
}.frame(width: 150)
}
}
断点打在VStack {那一行发现断点会走两次,但是断点打在checkLabel(with: viewStore.checkResult)那一行发现断点只会走一次,看起来第二次只是调用了一下,没有实际显示,所以没有触发checkLabel代码的执行,看起来问题不大。我仔细想了一下,store.scope(state: .counter内部如果是copy struct couter的话,所以实际触发CounterView刷新的就只是CounterView持有的store: Store<Counter, CounterAction>里边的Counter struct,跟父试图的无关,修改父视图的store里的counter struct应该确实不会对子视图产生刷新作用。
TCA - SwiftUI 的救星?(三) | OneV's Den
在上一篇关于 TCA 的文章中,我们看到了绑定的工作方式以及 Environment 在管理依赖和提供易测试性时发挥的作用。在这篇文章中,我们会继续深入,来看看 TCA 中的两个重要话题:Effect 角色到底是什么,以及如何通过组合的方式来把多个小 Feature 组合在一起,形成更加
https://onevcat.com/2022/03/tca-3/