From 0123f1d6f0672bc906b57c9725fd2788b21b89e1 Mon Sep 17 00:00:00 2001 From: rlittlesii <6969701+RLittlesII@users.noreply.github.com> Date: Tue, 28 Jan 2020 12:05:29 -0600 Subject: [PATCH 1/2] fix: Blazor ViewModel nullability during rendering --- .../ReactiveComponentBase.cs | 39 ++++++------- .../ReactiveLayoutComponentBase.cs | 39 ++++++------- src/ReactiveUI.Mac.sln | 58 +++++++++++++++++++ 3 files changed, 92 insertions(+), 44 deletions(-) diff --git a/src/ReactiveUI.Blazor/ReactiveComponentBase.cs b/src/ReactiveUI.Blazor/ReactiveComponentBase.cs index b096156a3f..b5350350ba 100644 --- a/src/ReactiveUI.Blazor/ReactiveComponentBase.cs +++ b/src/ReactiveUI.Blazor/ReactiveComponentBase.cs @@ -31,27 +31,6 @@ public class ReactiveComponentBase : ComponentBase, IViewFor, INotifyPrope private bool _disposedValue; // To detect redundant calls - /// - /// Initializes a new instance of the class. - /// - public ReactiveComponentBase() - { - var viewModelsPropertyChanged = this.WhenAnyValue(x => x.ViewModel) - .Where(x => x != null) - .Select(x => Observable.FromEvent( - eventHandler => - { - void Handler(object sender, PropertyChangedEventArgs e) => eventHandler(Unit.Default); - - return Handler; - }, - eh => x.PropertyChanged += eh, - eh => x.PropertyChanged -= eh)) - .Switch(); - - viewModelsPropertyChanged.Do(_ => InvokeAsync(StateHasChanged)).Subscribe(); - } - /// public event PropertyChangedEventHandler PropertyChanged; @@ -105,8 +84,24 @@ protected override void OnAfterRender(bool isFirstRender) { if (isFirstRender) { - this.WhenAnyValue(x => x.ViewModel).Where(x => x != null).Subscribe(_ => InvokeAsync(StateHasChanged)); + this.WhenAnyValue(x => x.ViewModel) + .Skip(1) + .Where(x => x != null) + .Subscribe(_ => InvokeAsync(StateHasChanged)); } + + this.WhenAnyValue(x => x.ViewModel) + .Where(x => x != null) + .Select(x => Observable.FromEvent( + eventHandler => + { + void Handler(object sender, PropertyChangedEventArgs e) => eventHandler(Unit.Default); + return Handler; + }, + eh => x.PropertyChanged += eh, + eh => x.PropertyChanged -= eh)) + .Switch() + .Do(_ => InvokeAsync(StateHasChanged)).Subscribe(); } /// diff --git a/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs b/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs index eac4991c8e..d687e91d12 100644 --- a/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs +++ b/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs @@ -29,27 +29,6 @@ public class ReactiveLayoutComponentBase : LayoutComponentBase, IViewFor, private bool _disposedValue; // To detect redundant calls - /// - /// Initializes a new instance of the class. - /// - public ReactiveLayoutComponentBase() - { - var viewModelsPropertyChanged = this.WhenAnyValue(x => x.ViewModel) - .Where(x => x != null) - .Select(x => Observable.FromEvent( - eventHandler => - { - void Handler(object sender, PropertyChangedEventArgs e) => eventHandler(Unit.Default); - - return Handler; - }, - eh => x.PropertyChanged += eh, - eh => x.PropertyChanged -= eh)) - .Switch(); - - viewModelsPropertyChanged.Do(_ => InvokeAsync(StateHasChanged)).Subscribe(); - } - /// public event PropertyChangedEventHandler PropertyChanged; @@ -102,8 +81,24 @@ protected override void OnAfterRender(bool isFirstRender) { if (isFirstRender) { - this.WhenAnyValue(x => x.ViewModel).Where(x => x != null).Subscribe(_ => InvokeAsync(StateHasChanged)); + this.WhenAnyValue(x => x.ViewModel) + .Skip(1) + .Where(x => x != null) + .Subscribe(_ => InvokeAsync(StateHasChanged)); } + + this.WhenAnyValue(x => x.ViewModel) + .Where(x => x != null) + .Select(x => Observable.FromEvent( + eventHandler => + { + void Handler(object sender, PropertyChangedEventArgs e) => eventHandler(Unit.Default); + return Handler; + }, + eh => x.PropertyChanged += eh, + eh => x.PropertyChanged -= eh)) + .Switch() + .Do(_ => InvokeAsync(StateHasChanged)).Subscribe(); } /// diff --git a/src/ReactiveUI.Mac.sln b/src/ReactiveUI.Mac.sln index e5cfaa32d4..17aee97e69 100644 --- a/src/ReactiveUI.Mac.sln +++ b/src/ReactiveUI.Mac.sln @@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.Testing.Tests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.AndroidX", "ReactiveUI.AndroidX\ReactiveUI.AndroidX.csproj", "{294D8C67-0181-41B4-AB28-0114CE3D6390}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.Blazor", "ReactiveUI.Blazor\ReactiveUI.Blazor.csproj", "{8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -584,6 +586,62 @@ Global {294D8C67-0181-41B4-AB28-0114CE3D6390}.Release|x64.Build.0 = Release|Any CPU {294D8C67-0181-41B4-AB28-0114CE3D6390}.Release|x86.ActiveCfg = Release|Any CPU {294D8C67-0181-41B4-AB28-0114CE3D6390}.Release|x86.Build.0 = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|Mixed Platforms.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|ARM.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|iPhone.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|Mixed Platforms.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|x64.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|x64.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|x86.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.AppStore|x86.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|ARM.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|ARM.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|iPhone.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|x64.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|x64.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|x86.ActiveCfg = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Debug|x86.Build.0 = Debug|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|Any CPU.Build.0 = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|ARM.ActiveCfg = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|ARM.Build.0 = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|iPhone.ActiveCfg = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|iPhone.Build.0 = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|x64.ActiveCfg = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|x64.Build.0 = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|x86.ActiveCfg = Release|Any CPU + {8D4DDE4A-56E2-4FB2-802C-92C8992E5CB1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From ad132420373a117a2e55d88f010358048327525f Mon Sep 17 00:00:00 2001 From: rlittlesii <6969701+RLittlesII@users.noreply.github.com> Date: Tue, 28 Jan 2020 12:11:43 -0600 Subject: [PATCH 2/2] formatting for readability --- src/ReactiveUI.Blazor/ReactiveComponentBase.cs | 3 ++- src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ReactiveUI.Blazor/ReactiveComponentBase.cs b/src/ReactiveUI.Blazor/ReactiveComponentBase.cs index b5350350ba..36fe286257 100644 --- a/src/ReactiveUI.Blazor/ReactiveComponentBase.cs +++ b/src/ReactiveUI.Blazor/ReactiveComponentBase.cs @@ -101,7 +101,8 @@ protected override void OnAfterRender(bool isFirstRender) eh => x.PropertyChanged += eh, eh => x.PropertyChanged -= eh)) .Switch() - .Do(_ => InvokeAsync(StateHasChanged)).Subscribe(); + .Do(_ => InvokeAsync(StateHasChanged)) + .Subscribe(); } /// diff --git a/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs b/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs index d687e91d12..e62ea9b400 100644 --- a/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs +++ b/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs @@ -98,7 +98,8 @@ protected override void OnAfterRender(bool isFirstRender) eh => x.PropertyChanged += eh, eh => x.PropertyChanged -= eh)) .Switch() - .Do(_ => InvokeAsync(StateHasChanged)).Subscribe(); + .Do(_ => InvokeAsync(StateHasChanged)) + .Subscribe(); } ///