Skip to content

Commit b1eb535

Browse files
Define IErrorBoundary as an internal interface for clarity
1 parent 1dac45a commit b1eb535

File tree

3 files changed

+31
-9
lines changed

3 files changed

+31
-9
lines changed

src/Components/Components/src/ErrorBoundaryBase.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Components
1111
/// <summary>
1212
/// A base class for error boundary components.
1313
/// </summary>
14-
public abstract class ErrorBoundaryBase : IComponent
14+
public abstract class ErrorBoundaryBase : IComponent, IErrorBoundary
1515
{
1616
// This deliberately doesn't inherit from ComponentBase because it's not intended to be
1717
// subclassable using a .razor file. ErrorBoundaryBase shouldn't be used as a base class
@@ -91,7 +91,7 @@ public Task SetParametersAsync(ParameterView parameters)
9191
/// <param name="exception">The current exception.</param>
9292
protected abstract void RenderDefaultErrorContent(RenderTreeBuilder builder, Exception exception);
9393

94-
internal void HandleException(Exception exception)
94+
void IErrorBoundary.HandleException(Exception exception)
9595
{
9696
if (_currentException is not null)
9797
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.AspNetCore.Components
7+
{
8+
// Purpose of this interface, instead of just using ErrorBoundaryBase directly:
9+
//
10+
// [1] It keeps clear what is fundamental to an error boundary from the Renderer's perspective.
11+
// Anything more specific than this is just a useful pattern inside ErrorBoundaryBase.
12+
// [2] It improves linkability. If an application isn't using error boundaries, then all of
13+
// ErrorBoundaryBase and its dependencies can be linked out, leaving only this interface.
14+
//
15+
// If we wanted, we could make this public, but it could lead to common antipatterns such as
16+
// routinely marking all components as error boundaries (e.g., in a common base class) in an
17+
// attempt to create "On Error Resume Next"-type behaviors.
18+
19+
internal interface IErrorBoundary
20+
{
21+
void HandleException(Exception error);
22+
}
23+
}

src/Components/Components/src/RenderTree/Renderer.cs

+6-7
Original file line numberDiff line numberDiff line change
@@ -330,14 +330,13 @@ private bool TrySendExceptionToErrorBoundary(IComponent? errorSource, Exception
330330
// Find the closest error boundary, if any
331331
while (componentState is not null)
332332
{
333-
if (componentState.Component is ErrorBoundaryBase errorBoundary)
333+
if (componentState.Component is IErrorBoundary errorBoundary)
334334
{
335-
// Force the IErrorBoundary component to clear its output, regardless of any logic inside
336-
// that component. This ensures that all descendants are cleaned up. We don't strictly have
337-
// to do this, since the only errors we handle this way are actually recoverable, so technically
338-
// it would be OK if the old output was left in place and continued operating. However that
339-
// would be a whole new way of keeping components running after failure which we don't want
340-
// to introduce and guarantee to support forever.
335+
// Even though ErrorBoundaryBase always removes its ChildContent from the tree when
336+
// switching into an error state, the Renderer doesn't rely on that. To guarantee that
337+
// the failed subtree is disposed, forcibly remove it here. If the failed components did
338+
// continue to run, it wouldn't harm framework state, but it would be a whole new kind of
339+
// edge case to support forever.
341340
AddToRenderQueue(componentState.ComponentId, builder => { });
342341

343342
try

0 commit comments

Comments
 (0)