-
-
Notifications
You must be signed in to change notification settings - Fork 105
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
Add ReactiveCommand<TInput, TOutput>
#249
Comments
I checked the ReactiveUI code, but it looks needlessly complicated and the performance seems to be very poor... By the way, is it not possible to use |
That's why we're looking at R3 for an alternative.
It very much depends on the use-case: file class Foo
{
public Foo(Observable<Bar> observable)
{
// 1) R3 command
observable
.Select(static bar => bar.R3Command.AsObservable())
.Merge()
.Subscribe(static unit => { /* no name */ });
// 2) R3 command with select
observable
.Select(static bar => bar.R3Command.Select(bar, static (_, bar) => bar.Name))
.Merge()
.Subscribe(static name => { /* */ });
// 3) ReactiveUI
observable
.Select(static bar => bar.ReactiveUICommand.ToObservable())
.Merge()
.Subscribe(static name => { /* */ });
}
}
file class Bar
{
public string Name { get; }
public R3.ReactiveCommand<R3.Unit> R3Command { get; }
public ReactiveUI.ReactiveCommand<System.Reactive.Unit, string> ReactiveUICommand { get; }
public Bar(string name)
{
Name = name;
R3Command = new R3.ReactiveCommand<R3.Unit>();
ReactiveUICommand = ReactiveUI.ReactiveCommand.Create<System.Reactive.Unit, string>(_ => Name);
}
} This is a common pattern in the application we're working on, where multiple instances of Take a file picker for example: file class Foo
{
public R3.ReactiveCommand<R3.Unit> PickFileCommand { get; }
public Foo()
{
PickFileCommand = new R3.ReactiveCommand<R3.Unit>(async (_, cancellationToken) =>
{
var path = await PickFileAsync(cancellationToken);
});
}
private async ValueTask<AbsolutePath> PickFileAsync(CancellationToken cancellationToken) => throw new NotImplementedException();
} The command gets bound to a button on the UI, but here we want to do something with the output. Of course, we can just add a public R3.ReactiveCommand<R3.Unit> PickFileCommand { get; }
private R3.Subject<AbsolutePath> PickedFileSubject { get; } = new();
public Foo()
{
PickedFileSubject
.Where(path => path.FileExists)
.Subscribe(path => { /* do stuff */ });
PickFileCommand = new R3.ReactiveCommand<R3.Unit>(async (_, cancellationToken) =>
{
var path = await PickFileAsync(cancellationToken);
PickedFileSubject.OnNext(path);
});
} This gets us where we want, but it's not nice to use. I hope this illustrates why you might want an output on the command. It's not the end of the world for us, but it would make our lives much easier. |
Thanks for the detailed explanation, I understand now. |
Thanks, will try it out. |
Works great! |
R3 has
ReactiveCommand<TInput>
which inherits fromObservable<TInput>
. The observable contains the inputs, but there's no variant that also has outputs:Instead of passing
Action<TInput>
, this variant would useFunc<TInput, TOutput>
. My team has been using the ReactiveUI counterpart with outputs everywhere because it makes certain UI code much simpler. Since reactive commands are observables, we merge these observables to be able to react to a list of commands in situations where we work with lists or trees of items.The text was updated successfully, but these errors were encountered: