Closed alexandrutatarciuc closed 1 year ago
If you can upload a repro somewhere, I'll have a look for you.
PS: HttpClient should be registered as scoped.
First, I am sorry a late response. I had more time to look into it. It looks like this issue appears to happen when you are using the fluxor state in a DI scope that's different than the app's scope (root DI scope I guess).
If you have a service that must be added to a component's scope in Blazor, like a ViewModel, then you should inherit from OwningComponentBase
and use ScopedServices
to call GetService
to set the ViewModel value. By doing this, as far as I understand, you bound the service to the component's scope so now IDisposable.Dispose
on that service will actually be called when the component is disposed.
If I try to inject IState<MyState>
in the ViewModel, they'll be no changes to the value of the state. In fact, if I inject the IDispatcher and dispatch an action, the reducer will never be called as well.
The same issue happens to be for a service that derives from DelegatingHandler, because the scope of that service isn't the root any longer, but the HttpClient's DI scope (or request's scope, I am not sure).
Am I missing any option when registering Fluxor with .AddFluxor()
or do you have any clue how to make a workaround for that? I will try to send a repro tomorrow.
OwningComponentBase creates its own scope. I don't think the child of a scope inherits instances from its parent.
Why are you using OwnedComponentBase for a view model? And shouldn't your state be your view model?
OwningComponentBase creates its own scope.
Yes indeed. This is probably why I can inject the state, but it doesn't get updated.
I don't think the child of a scope inherits instances from its parent.
I am not really sure what you mean by that.
Why are you using OwnedComponentBase for a view model?
Because I want my components to control the lifetime of the ViewModel service. I want to be able to implement IDisposable on the ViewModel when needed. If I don't register the ViewModel as a service scoped to the component's lifetime, it wouldn't be called when the component gets disposed. I want "to marry" the component and the ViewModel, so that the ViewModel gets disposed when the component does.
And shouldn't your state be your view model?
My usage of "ViewModel" probably doesn't fall under the common definition of a ViewModel in a MVVM context. It is more like a code-behind class where I store all the logic related to a component so it is easier to test my components (because I can mock them). Therefore, every component has one and only one unique ViewModel, however different ViewModels of different components can inject the same State. For example, I can have components Product, AddToCartButton, etc. each with their unique ViewModel, but they are all interested in the CartState, so the Product would display that it is already added to the cart and the AddToCartButton would display an increment in quantity of the product. So no, my state isn't the ViewModel.
But as I mentioned earlier, the same issue occurs for a handler deriving from DelegatingHandler. And now you can clearly see why some would do that because developers would intend to get some sort of key or id from their UserState to append as a value to a header in every request they are making to the backend.
Instead of having the component dispose, try this
I'm confused. The issue is that Fluxor seems to not work at all when it is injected in a service that has a different scope than the application's root scope (thus, step 2 is not possible because the reducer will never be called). I need something to let Fluxor know that I also want new state changes for my DI scope.
Intuitively, I think that this should be added to Fluxor as a new feature. If you can point out why this is happening in Fluxor, I can contribute and add this new capability.
It's not Fluxor, it's just how .Net dependency injection works. The Unity DI used to have HierarchicalLifetimeManager, but the modern one doesn't.
Why not use my suggested approach instead?
I am just not sure what you meant.
In my example provided in the repro, I have CounterState
, a Counter
component, an ICounterViewModel
, and MyComponentBase
from which my Counter
component derives from.
Do you suggest removing MyComponentBase and disposing the ViewModel manually using ViewModel = ViewModel.Empty
?
I assume this because I don't need my state to be cleared or disposed so I think you meant the ViewModel.
I want to reiterate this again, I can possibly find a workaround for this issue by not registering my ViewModel using ScopedServices
from OwningComponentBase
(even though this is what Microsoft seems to recommend doing), but if I have a custom message handler that derives from DelegatingHandler
, the Fluxor State doesn't work there either. It would be really really annoying if I can't use a message handler for my HTTP requests.
If it's an object instance per component you need, why not new it manually and Dispose?
So I solved this inconvenience by avoiding inheriting from OwningComponentBase
and disposing manually just as you suggested. Do you have any idea/suggestion on how I can use the state inside of an HTTP message handler that derives from DelegatingHandler
? (it has its own DI scope)
Sorry, you're on your own with that one :)
Thanks a lot for the help!
I have a message handler that inherits from DelegatingHandler and I use it for the HTTP client. The service itself is registered as transient. In my message handler I inject
IState<MyState>
(constructor dependency injection) and in the constructor I hook intoMyState.StateChanged += HandleStateChanged
. However, my state is always null and myHandleStateChanged
method is never called. If I use my message handler as a service by injecting it, it works just fine, but if it is used by the HTTP client as a delegating handler - it doesn't work. My use case is that I have an id in MyState and I want all of my requests to contain a custom header with the id as the value. I've been struggling to understand what is the main problem, any guidance is appreciated.