Skip to content

[Bug]: Blazor component base classes do not trigger WhenActivated() #3413

@rcdailey

Description

@rcdailey

Describe the bug 🐞

When deriving my Page (view) from ReactiveInjectableComponentBase and providing a view model class that implements IActivatableViewModel, my callback provided to this.WhenActivated() is not invoked for me.

Step to reproduce

  1. Derive your page from the mentioned base class like so:

    @inherits ReactiveInjectableComponentBase<MonitorDeploymentsViewModel>
  2. Implement your view model like this:

    public class MonitorDeploymentsViewModel : ReactiveObject, IActivatableViewModel
  3. Provide a call to WhenActivated like so:

    this.WhenActivated(disposal =>
    {
        // Do something meaningful here
    });
  4. Compile and run the application. Be sure to visit the page we're talking about to trigger the issue.

Reproduction repository

This is a non-public repository; I have no MCVE.

Expected behavior

The ReactiveInjectableComponentBase class (as well as the other Blazor-specific component base classes) implement ICanActivate and already trigger the following:

  • When OnInitializedAsync() is invoked by the view, trigger Activated state.
  • When Dispose() happens (IDisposable), trigger Deactivated state.

However, by default, there are no observers for these states. This is because, in my opinion, in the setter for ReactiveInjectableComponentBase.ViewModel, code is missing to check if the supplied VM implements IActivatableViewModel and if so, attaches it to the appropriate Activated/Deactivated observables.

The workaround right now requires users to manually override OnInitializedAsync() simply to invoke ViewModel.Activator.Activate(), as well as the corresponding Dispose() override for the Deactivate(). This means that, ultimately, there's logic in the ComponentBase class that ends up not getting used at all because it's not tied in.

I think the addition necessary to the base ViewModel property is as follows:

            _viewModel = value;
            if (_viewModel is IActivatableViewModel avm)
            {
                Activated.Subscribe(_ => avm.Activator.Activate());
                Deactivated.Subscribe(_ => avm.Activator.Deactivate());
            }

Another solution I tested that seems to work:

protected override void OnInitialized()
{
  Activated.Subscribe(_ => ViewModel!.Activator.Activate());
  Deactivated.Subscribe(_ => ViewModel!.Activator.Deactivate());
  base.OnInitialized();
}

Screenshots 🖼️

No response

IDE

Rider Windows

Operating system

Windows

Version

10

Device

N/A

ReactiveUI Version

18.3.1

Additional information ℹ️

I created this issue at the request of @glennawatson, who discussed this with me in Slack.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions