Skip to content

Latest commit

 

History

History
254 lines (169 loc) · 11.4 KB

RxUI.md

File metadata and controls

254 lines (169 loc) · 11.4 KB

ReactiveUI CheatSheet

A collection of links and snippets to resources, samples, answers, people, videos, etc. related to ReactiveUI.

Table of Contents

Sample Projects

Most of these are pretty dated but still have a lot of relevance.

A lot of these are taken from this thread, but I find it easier to browse when it's organized by platform, like this.

Xamarin.Forms

WorkoutWotch - Xamarin Forms Video Series - The accompanying video series, referenced in the README, covers Xamarin iOS Native, but the codebase was later converted to Xamarin Forms to support other platforms.

Reactive Examples by TheEightBot

UnofficialGitterApp

XamarinEvolve2014 - Heavily commented demo app by Ani Betts

Android

Burger ingredient streams demo with blog post

Espera.Mobile

iOS

WorkoutWotch - Xamarin Forms Video Series - The accompanying video series, referenced in the README, covers Xamarin iOS Native, but the codebase was later converted to Xamarin Forms to support other platforms.

CodeHub - uses MVVMCross

ReactiveTableView & ReactiveCollectionView Demo

WPF

Sample code for the book "You, I, and ReactiveUI"

FirstsStepsRUI with blog post

UWP

RxUI-UWP-Sample

Learning Resources

Github Repo

ReactiveUI Slack

ReactiveUI Docs

Book: You, I, and ReactiveUI - Love this book. Highly recommended.

Custom Routing in ReactiveUI

Videos

Reactive Extensions for .NET Developers with Michael Stonis (November 2018)

Why You Should Be Building Better Mobile Apps with Reactive Programming – Michael Stonis

Tips & Best Practices

Inject Service Locator interface via constructor

public SuspensionHost(ISuspensionDriver driver = null)
{
    driver = driver ?? Locator.Current.GetService<ISuspensionDriver>();
}

Explanation: This uses a Service Located interface for the default interface, but only if the caller didn't give an explicit one in the constructor. Far more straightforward to test in a unit test runner than trying to construct a sham IoC container, but still falls back to a default implementation at runtime.

Source: https://stackoverflow.com/a/26924067/5984310

Call async operations in the View constructor, rather than the ViewModel constructor.

this.WhenAnyValue(x => x.ViewModel.LoadItems)
    .SelectMany(x => x.ExecuteAsync())
    .Subscribe();

Explanation: Invoking async operations in the ViewModel constructor means that your ViewModel class becomes more difficult to test, because you always have to mock out the effects of calling LoadItems, even if the thing you are testing is unrelated.

Source: https://codereview.stackexchange.com/a/74793

When should I bother disposing of IDisposable objects?

  1. No need
public MyViewModel()
{
    MyReactiveCommand
        .Execute()
        .Subscribe(...);
}

Quote by Kent Boogart (one of the ReactiveUI maintainers):

When the execution of a ReactiveCommand completes, all observers are auto-unsubscribed anyway. Generally, subscriptions to pipelines that have a finite lifetime (eg. via a timeout) need not be disposed manually. Disposing of such a subscription is about as useful as disposing of a MemoryStream.

  1. Do dispose
public MyView()
{
    this.WhenAnyValue(x => x.ViewModel)
        .Do(PopulateFromViewModel)
        .Subscribe();
}

This one is tricky. Disposing of this subscription is a must if developing for a dependency property-based platform such as WPF or UWP. Quoting Ani Betts, this is because "there's no non-leaky way to observe a dependency property," which is exactly what the ViewModel property of a ReactiveUserControl is. However, if you happen to know that your ViewModel won't change for the liftime of the view then you can make ViewModel a normal property, eliminating the need to dispose. For other platforms such as Xamarin.Forms, Xamarin.Android, and Xamarin.iOS there's no need to dispose because you're simply monitoring the property (ViewModel) on the view itself, so the subscription is attaching to PropertyChanged on that view. This means the view has a reference to itself and thus, doesn't prevent the it from being garbage collected.

  1. Do dispose
public MyViewModel()
{
    SomeService.SomePipeline
        .Subscribe(...);
}

Services commonly have a longer lifetime than view models, especially in the case of singletons and global application variables. Therefore, it's vital that these kinds of subscriptions are disposed of.

  1. No need
public MyViewModel()
{
    SomeService.SomePipelineModelingAsynchrony
        .Subscribe(...);
}

Pipelines modeling asynchrony can be relied upon to complete, and thus the subscription will be disposed of automatically via OnComplete (or OnError).

  1. Do dispose
public MyView()
{
    this.WhenAnyValue(x => x.ViewModel.SomeProperty)
        .Do(AssignValueToViewControl)
        .Subscribe();
}

Now you're saying "attach to PropertyChanged on this and tell me when the ViewModel property changes, then attach to PropertyChanged on that (the view model) and tell me when SomeProperty changes." This implies the view model has a reference back to the view, which needs to be cleaned up or else the view model will keep the view alive.

  1. Performance tip
public MyView()
{
    // For a dependency property-based platform such as WPF and UWP
    this.WhenActivated(
        disposables =>
        {
            this.WhenAnyValue(x => x.ViewModel)
                .Where(x => x != null)
                .Do(PopulateFromViewModel)
                .Subscribe()
                .DisposeWith(disposables);
        });
        
        // For other platforms it can be simplified to the following
        this.WhenAnyValue(x => x.ViewModel)
            .Where(x => x != null)
            .Do(PopulateFromViewModel)
            .Subscribe()
}

private void PopulateFromViewModel(MyViewModel vm)
{
    // Assign values from vm to controls
}

More efficient than binding to properties. If your ViewModel properties don't change over time, definitely use this pattern. The WhenActivated part is important for dependency property-based platforms (as mentioned in case 2) since it will handle disposing of the subscription every time the view is deactivated.

  1. No need
// Should I dispose of the IDisposable that WhenActivated returns?
this.WhenActivated(
    disposables =>
    {
        ...
    })

Quote by Kent Boogart:

If you're using WhenActivated in a view, when do you dispose of the disposable that it returns? You'd have to store it in a local field and make the view disposable. But then who disposes of the view? You'd need platform hooks to know when an appropriate time to dispose it is - not a trivial matter if that view is reused in virtualization scenarios. In addition to this, I have found that reactive code in VMs in particular tends to juggle a lot of disposables. Storing all those disposables away and attempting disposal tends to clutter the code and force the VM itself to be disposable, further confusing matters. Perf is another factor to consider, particularly on Android.

Notable People to Follow

Ani Betts @anaisbetts

Kent Boogaart @kent_boogaart

ReactiveUI Twitter

Stack Overflow Top Users for ReactiveUI

ReactiveUI Glossary

WhenActivated

WhenActivated: allows you to specify the things that should occur when a view or view model is activated and deactivated; requries that our view implements IActivatable; Typically, you don't need to worry about disposing of the disposable returned by WhenActivated. Views tend to deactivate naturally as a consequence of users navigating through your application and ReactiveUI's default IActivationForViewFetcher implementations.

IActivatable: think of it as IActivatableView; implemented by IViewFor; tag interface (no methods to implement)

ISupportsActivation: think of it as IActivatableViewModel; requires that the IViewFor invokes WhenActivated; can test view model activation and deactivation by calling Activate and Deactivate; implementing this interface more than once in a view model class hierarchy will result in view model activation failing to work correctly

ViewModelActivator: essentially a sink in which WhenActivated will register the blocks of activation logic provided by your view model

IActivationForViewFetcher: implements GetAffinityForView and GetActivationForView

GetAffinityForView: method of IActivationForViewFetcher; tells ReactiveUI how confident you are that your implementation of IActivationForViewFetcher can provide activation information for a given view; higher numbers returned by this method trump lower numbers returned by other implementations

GetActivationForView: method of IActivationForViewFetcher; returns IObservable that ticks true when the view is activated and false when the view is deactivated

NAVIGATION

RoutingState: NavigationStack, Navigate (ReactiveCommand), NavigateBack (ReactiveCommand), NavigateAndReset (ReactiveCommand)

IScreen: Router (RoutingState); root of a navigation stack; despite the name, its views don't have to occupy the whole screen

IRoutableViewModel: UrlPathSegment (string), HostScreen (IScreen)

RoutedViewHost: platform-specific; monitors an instance of RoutingState, responding to any changes in the navigation stack by creating and embedding the appropriate view