-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Removing selectors in subscriptions / dependant subscriptions #96
Comments
Hi Marius! Well, this is a curious use case you showed me there. Before we dig in, can you explain me why you need to do "inner" observable this way? I ask you that because using a Subscribe in a Subscribe is always a bad move. To complete my answer, I recommend you to use the |
I agree that having inner Subscribes seems fishy at first, but I think it is unavoidable. Also it works well without CreateSelector and just Select/Subscribe. |
The |
Yes, that is exactly how I understood it, but that is just syntactic sugar on what I am already doing. I'm pretty sure the test case will fail with TakeUntil as well. |
Well, I believe your problem is that your selector crashes because |
No, that is not the problem. The selector is a way to select the Thing so I do not have to duplicate that logic for each detail in it. In the real world, there are lots (100+) of details for each Thing. And there is not just 0-1 Things (which a nullable reference can represent), there are 0-N. Hence accessing it is not merely accessing a reference, but looking it up in a list or a dictionary, akin to the lenses we discussed a while back - but this time on the "view" side of things. I need a way to deduplicate that logic and use it at the right level of abstraction. |
I've looked into this some more, and it seems Rx.NET |
Some more research, and it seems changing the Selector a little does the trick:
Not entirely sure how it'd make sense to integrate this into the library. Does it make sense to add this directly to the store's observable? If I understand correctly, the refcount would always just be 0 or 1 when integrating it in the selector. |
Oh yes. The ref count internally keep the number of subscribers curently using this Observable source. So it can be 0 to N. When the number of subscribers hits 0, the source does not emit new values. The good part is that, in theory, you can create your own operator in your code and apply that new operator when you need it. |
Yea, I'm already doing that, and it fixes my crash! |
So while the Share aka Publish/RefCount method prevents my crashes, it does not work because recursive Subscriptions do not |
Oops, sorry about the close&repoen. So it seems that the Publish/RefCount method only works in preventing the crashes, but I had other inconsistencies using them. I suspect it has to do with recursive Subscriptions not working well with them.
I suspect I have not covered all the edge cases in my implementation, but it works pretty nicely already. You interested in that? I can put it in a gist or something. It's about 300 LOC |
Well, that may be a good idea, if anyone has the same problem as you in the future. But integrating it in ReduxSimple, that's another topic. I still struggle to understand the use case so I can't imagine for other people. :) |
By "my use case" you mean the nested subscriptions? Maybe I'm doing something wrong, but I don't see how. How about a simple example:
With these reducers:
I observe it like this:
It will crash if I do something like:
Or even just Create and Delete. However, it will not crash, if I change my "detail" connection to:
But I will get too many updates that way, and it'll duplicate the selection logic into the subscription, which is bad if you have more properties than just name. |
Oh, well. I think I start to understand what you try to achieve. So may I suggest this code? store
.Select(state => state.Thingies)
.SelectMany(list => list)
.GroupBy(thing => thing.Id)
.DistinctUntilChanged()
.Subscribe(thing => Console.WriteLine("Thingy {0} has name {1}", thing.Id, thing.Name)); What do you think? |
Well that at least pointed me in the direction of how Rx.NET handles observables of sequences, so thanks for that. |
Well, you can filter actions and listen to |
That would be very un-Redux'y. The view should be derived completely from the State, never from the action stream. |
Well, not really. I do that very often. It really depends on your use case. Sometimes it is way simpler to execute a function based on an action than to observe a state value which has no meaning (only to execute a function). |
That's not how js redux sees it (e.g. first sentence here: https://redux.js.org/faq/design-decisions#why-doesnt-redux-pass-the-state-and-action-to-subscribers) or how I see it. But we can agree to disagree. |
Sorry it took so long, been a busy year. I wrote a blog article how we're using ReduxSimple and posted the 'SelectorGraph' code that can do selection caching and guarantees proper order for nested selections here: https://github.com/softwareschneiderei/WpfReduxSample/blob/main/WpfReduxSample/Reduxed/SelectorGraph.cs |
Wow, you took that thing very seriously. I am really proud you cited this lib ReduxSimple and going beyond with the Odonno's Ctinematic Universe :) (talking about Converto even if it is really outdated now). So still, I don't see that feature needed in ReduxSimple. Like you guessed, the goal is to make it damn simple, as much as possible. However, I see that you made a serious effort on WPF redux-like architecture and I would like you to share this expertise with the community. I wonder if you'd be interested in writing some libraries in the project? ex: ReduxSimple.Wpf (a collection of UI helpers), ReduxSimple.Wpf.DevTools, ReduxSimple.Wpf.RouterStore, etc.. |
Hi there,
We have a CRUD-type application where we have an "item" UI control that only exists when there is a selected item. Now when we delete the last item in the list, the application crashes while erxecuting a selector we created with CreateSelector that does something like
state => state.CurrentItem.Id
becausestate.CurrentItem
isnull
. The thing is, that this selector should not be executed, because we alreadyDispose()
ed all subscriptions to it (we remove andDispose()
of the UI control in an "outer" subscription). Interstingly, the code in the innerSubscribe()
is not executed, which is correct.I reckon this is a fairly common pattern when mapping any kind of list to UI controls.
Here's a test case illustrating the problem:
Sorry, I was not able to make the test case simpler.
It seems this case is difficult because you need to remove a selector while already reacting to other selectors. But the selector really should not be executed when the subscribtion is not executed. For this to be well defined, subscriptions should be ordered/executed in the order their
Subscribe
calls are beginning. Obviously, in this case, the other Subscribe calls the inner Subscribe, so it is important to distinguish between the beginning and end of the call. And with this ordering, "outer" subscriptions would preceed inner subscriptions.The text was updated successfully, but these errors were encountered: