Skip to content

AsNonRenderingEventHandler + ErrorBoundary = unexpected behavior #54543

Closed
@ScarletKuro

Description

@ScarletKuro

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Hello.

In MudBlazor we recently did this change: https://github.com/MudBlazor/MudBlazor/pull/8203/files#diff-7258903903d30bdc973979ca0d64c61adbee3f4441e47749100907f522060a47
We added AsNonRenderingEventHandler (that was copied from official MS document) to our internal onclick and now the ErrorBoundary doesn't catch the exceptions that are fired within the AsNonRenderingEventHandler event:

<ErrorBoundary>
    <ChildContent>
        <MudButton OnClick="ProcessSomething" Variant="Variant.Filled" Color="Color.Primary">
            <MudText>Click me</MudText>
        </MudButton>
    </ChildContent>
    <ErrorContent Context="exc">
      <MudAlert Severity="Severity.Error">Exception: @exc.Message</MudAlert>
    </ErrorContent>
</ErrorBoundary>
@code {
    async Task ProcessSomething()
    {
        throw new ApplicationException("TESTING");
    }
}

You will see in console:

crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: TESTING
System.ApplicationException: TESTING
   at Try.UserComponents.__Main.ProcessSomething()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at MudBlazor.MudBaseButton.OnClickHandler(MouseEventArgs ev)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

rather than the ErrorContent.

The weird thing about it if that I use alternative to AsNonRenderingEventHandler:

public abstract class MudBaseButton : MudComponentBase, IHandleEvent
{
    Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg) => callback.InvokeAsync(arg);
}

Then everything will work as expected and ErrorBoundary is working
another weird thing is that if I change this:

public static Func<TValue, Task> AsNonRenderingEventHandler<TValue>(Func<TValue, Task> callback) => new AsyncReceiver<TValue>(callback).Invoke;

to this:

public static Func<TValue, Task> AsNonRenderingEventHandler<TValue>(Func<TValue, Task> callback)
{
	var receiver = new AsyncReceiver<TValue>(callback);
	return async value => await receiver.Invoke(value);
}

Then the ErrorBoundary will catch the exception as well, but the problem is that this return async value => await receiver.Invoke(value); and this return new AsyncReceiver<TValue>(callback).Invoke should be EQUIVALENT in C# (except that for first one an asyncstatemachine is created, but end result should be the same and no side effects).

upd: well, with this snippet https://try.mudblazor.com/snippet/cOQSERlIheRAyAJe I just found out that async value => await receiver.Invoke(value) doesn't prevents re-rendering, but the IHandleEvent implementation in component works, so this doesn't explain few moments:

  1. Why with the original EventUtil from docs the ErrorBoundary doesn't work.
  2. Why with my offered version the exception flows, but the re-render is not prevented.
  3. Why if I implement IHandleEvent in the component it works great - the exception flows and no re-render is happening, but the EventUtil in 1. or 2. doesn't work as I expect?

I described the same in this in MudBlazor issue: MudBlazor/MudBlazor#8365 (comment)

@SteveSandersonMS could you please explain, as I'm very confused on what's going on.

Expected Behavior

ErrorBoundary to work with AsNonRenderingEventHandler

Steps To Reproduce

This would require to fork the https://github.com/MudBlazor/MudBlazor repository and play with the MudButton.razor and MudButtonBase and the snippet from https://try.mudblazor.com/snippet/caQSaxvSoRhvMaSP (added very minimal repro below, so not relevant anymore)

upd: Minimal reproduction without MudBlazor:
BlazorEventUtilsIssue.zip

Exceptions (if any)

crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: TESTING
System.ApplicationException: TESTING
   at Try.UserComponents.__Main.ProcessSomething()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at MudBlazor.MudBaseButton.OnClickHandler(MouseEventArgs ev)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

.NET Version

8.0.103

Anything else?

No response

Metadata

Metadata

Assignees

Labels

DocsThis issue tracks updating documentationNeeds: Attention 👋This issue needs the attention of a contributor, typically because the OP has provided an update.area-blazorIncludes: Blazor, Razor Componentsinvestigate

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions