if I have substate1, substate2 and uiviews filtered subscribers view1, view2 in one ViewController.
Now when I change substate2, the newState() is being fired for the view2 and for the view1, although substate1 isn't changed. To fix this I confirms the substates to Equatable protocol and when the newState() is fired check if the new substate is equal to the old one and if it's not then change the view properties.
Is there more practical way to do that?
ReduxTest.zip
I'm also wondering the same - this seems to be handled by Redux in JS, but not in Swift
Unfortunately handling it manually in vanilla ReSwift is what's necessary right now. It could be handled automatically but then we'd need to mandate that all StateType objects (and all state within those objects) conform to Equatable, which isn't a terrible idea.
I agree! The alternative I suppose is to use Observables for every sub-state, so we can subscribe to a specific state, or even variables within that state.
Somewhat combining redux / rxstore principles.
Being able to subscribe to sub-states individually is crucial for effective use of this pattern IMO. Would be awesome to see it implemented! 馃檶
I'm making a game using ReSwift, and I think it should be top priority, or maybe document something as a workaround if it can't be builtin. This is very crucial for efficient UI refresh or minimal update.
My use case is that I have an UIState, which contain what scene is the current scene and what modal should be presented on screen.
But I also have a timer in another state, which can be called multiple time per second, and so the newState function is called every time I update timer related date, even if I don't subscribe to that specific state.
Any new on the Equatable merge request?
@Dimillian Hey! Sorry that PR has been delayed by me traveling + catching up with life afterwards.
Did you take a look at the PR yet? Do you think the suggested API would solve your problem? If you have time it would be great to try to include that specific branch in your project and test it.
@Ben-G Yes, I find the idea and implementation well done, actually this is somehow what I did as a workaround, I have a refreshUi flag inside my state and it's only true when something on the UI have changed within my state, which is an Equatable comparaison between my previous state and the new one.
So yes, making every part of my state conform to Equatable and then dispatch the new one only if it's different seems to be a quite perfect solution for my use case!
I'll try this branch on my project this weekend if I have some free time.
FWIW, In ReactiveReSwift + RxSwift, you can accomplish this easily with
store.observable.map { $0.subStateOfInterest }.distinctUntilChanged()
So I noticed this was added to master with https://github.com/ReSwift/ReSwift/pull/203
I was trying out on my project, and the Subscriber is still being notified for every state update.
mainStore.subscribe(self) { state in state.childState }
extension CollectionViewController: StoreSubscriber {
typealias StoreSubscriberStateType = ChildState
func newState(state: ChildState) {
self.collectionView?.reloadData()
}
}
@dccarmo According to your snippet you're not yet using master. The syntax for substate selection has changed, and the old syntax will cause a compiler error.
new syntax:
mainstore.subscribe(self) { $0.select { state in state.childState } }
With that, if ChildState conforms to Equatable, then you'll get the functionality you want.
If ChildState cannot conform to Equatable, then you can manually implement skipRepeats:
``swift
mainstore.subscribe(self) {
$0.select { state in state.childState }
.skipRepeats { oldState, newState in
// returntrue` to skip newState call
}
}
@mjarvis Oh I see! I guess I skipped the wrong bits on the PR, it seemed the docs were updated as well (https://github.com/ReSwift/ReSwift/blob/master/Docs/Getting%20Started%20Guide.md#example-with-filtered-subscriptions) but maybe not.
Thanks!
@mjarvis somehow using the new syntax for substate selection, so in my case:
mainStore.subscribe(self) {
$0.select {
($0.flightState)
}
}
gives me an error:
Type of expression is ambiguous without more context
..the substate conforms to StateType and Equatable.
Extend the subscriber to implement the protocol, declaring your substate in the function param and that will help the compiler to resolve its ambiguity.
I'm also seeing Type of expression is ambiguous without more context. Is there something I haven't wired up properly?
// store
struct State: StateType {
var channels: [Channel]
var tags: [Tag]
}
class ChannelViewController: UIViewController {
// ...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
store.subscribe(self) { $0.select { state in state.channels } } // **error**
}
}
extension ChannelViewController: StoreSubscriber {
func newState(state: [Channel]) {
self.channels = state
tableView.reloadData()
}
}
Does the error change when you add type annotations?
store.subscribe(self) { $0.select { (state: State) -> [Channel] in state.channels } }
yes, now I'm getting the error Cannot invoke 'select' with an argument list of type '((State) -> [Channel])'. My Swift-fu isn't that great; sorry if I'm missing something obvious.
If it's helpful, here are my reducers:
func appReducer(action: Action, state: State?) -> State {
return State(
channels: channelsReducer(state: state?.channels, action: action),
tags: tagsReducer(state: state?.tags, action: action)
)
}
func channelsReducer(state: [Channel]?, action: Action) -> [Channel] {
var state = state ?? []
switch action {
case let action as CreateChannel:
let channel = action.channel
state.append(channel)
return state
case let action as SetChannels:
let channels = action.channels
return channels
default:
return state
}
}
so! it turns out I hadn't updated my Cocoapods repos and was working off of ReSwift 3.0. Upgrading to 4.0 means the line store.subscribe(self) { $0.select { state in state.channels } } now works fine without the annotations. Thanks!
Great to hear :) Closing due to inactivity from the OP.
Most helpful comment
Being able to subscribe to sub-states individually is crucial for effective use of this pattern IMO. Would be awesome to see it implemented! 馃檶