-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
InteractionBinder: immediately unregister interaction handlers when ViewModel becomes null #4140
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
Conversation
…Root cause: Reflection.ViewModelWhenAnyValue filters out null ViewModel values, so the binder never saw a null and didn’t dispose the previous registration. This left the handler attached after ViewModel was set to null, causing Interaction.Handle to succeed instead of throwing UnhandledInteractionException.\n\nFix: Merge a null-emitting stream based on view.WhenAnyValue(x => x.ViewModel) into the interaction property stream, and set the SerialDisposable to Disposable.Empty when null is observed. Applied to both Task and Observable handler overloads.\n\nTests: ReactiveUI.Tests.InteractionBinding.InteractionBinderImplementationTests now pass (20/20). Ran entire ReactiveUI.Tests namespace: 294/294 passed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Fixes immediate unregistration of Interaction<TInput,TOutput> handlers when a view’s ViewModel becomes null so subsequent Handle(...) calls correctly throw UnhandledInteractionException<,>.
- Emit a null interaction when ViewModel transitions to null and merge it into the binding stream.
- Replace WhereNotNull() with logic that disposes the current registration on null emissions via SerialDisposable.
- Adjust test parallelization attributes and csproj condition for WinUI.
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
File | Description |
---|---|
src/ReactiveUI/Bindings/Interaction/InteractionBinderImplementation.cs | Merge a VM-null stream into binding source and dispose handler on null to unregister immediately. |
src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj | Fix ItemGroup condition to only include CsWinRT on Windows targets. |
src/ReactiveUI.Builder.Tests/ReactiveUIBuilderCoreTests.cs | Mark test fixture NonParallelizable. |
src/ReactiveUI.Builder.Tests/ReactiveUIBuilderBlockingTests.cs | Mark test fixture NonParallelizable. |
src/ReactiveUI.Builder.Tests/AssemblyInfo.Parallel.cs | Reduce assembly test parallelism to 1. |
src/ReactiveUI.Builder.Maui.Tests/AssemblyInfo.Parallel.cs | Reduce assembly test parallelism to 1. |
Comments suppressed due to low confidence (1)
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4140 +/- ##
===========================================
- Coverage 59.03% 37.16% -21.88%
===========================================
Files 160 138 -22
Lines 5847 5686 -161
Branches 1031 852 -179
===========================================
- Hits 3452 2113 -1339
- Misses 2007 3357 +1350
+ Partials 388 216 -172 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
What’s fixed
When a view’s
ViewModel
is set tonull
, any previously boundInteraction<TInput,TOutput>
handlers are now unregistered immediately. This restores the expected behavior where callingInteraction.Handle(...)
after clearing theViewModel
throws anUnhandledInteractionException<,>
.Symptoms
After
view.ViewModel = null;
, invokingvm.Interaction1.Handle("123")
still hits the old handler instead of throwing.NUnit tests like:
fail with “But was: no exception thrown”.
Before vs After
Before
InteractionBinderImplementation
only reacted to non-nullViewModel
transitions.ViewModel
becamenull
, the binding stream didn’t emit a value to trigger disposal, so the previously registered handler could remain attached.After
We explicitly merge a “VM became null” stream into the binding sequence:
ViewModel
isnull
, we emit adefault(IInteraction<,>)
value.SerialDisposable
) on that emission.Result: as soon as the
ViewModel
is cleared, handlers are unregistered and subsequentHandle(...)
calls correctly surfaceUnhandledInteractionException<,>
.Technical summary
Added:
On each emission we set
interactionDisposable.Disposable
to either the new handler registration orDisposable.Empty
whennull
is seen, which disposes the prior registration.Implemented in both overloads (
Task
andIObservable<TDontCare>
handlers).Impact
ViewModel = null
.How to verify
Bind an interaction handler.
Set
ViewModel
tonull
.Call
Interaction.Handle(...)
.UnhandledInteractionException<,>
is thrown.Run the test suite. Previously failing tests like:
UnregisterTaskHandlerWhenViewModelIsSetToNull
UnregisterObservableHandlerWhenViewModelIsSetToNull
should pass.
Compatibility & risk
ViewModel
tonull
.ViewModel
(unlikely and contrary to docs/intent), behavior will now be corrected.