[Blazor] Prevents HtmlRenderer from calling OnAfterRender by default
This commit is contained in:
parent
706309f266
commit
521cabc545
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.ExceptionServices;
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Components.RenderTree;
|
using Microsoft.AspNetCore.Components.RenderTree;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -21,6 +22,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
||||||
"area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"
|
"area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly Task CanceledRenderTask = Task.FromCanceled(new CancellationToken(canceled: true));
|
||||||
|
|
||||||
private readonly Func<string, string> _htmlEncoder;
|
private readonly Func<string, string> _htmlEncoder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -40,7 +43,19 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
|
protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
// By default we return a canceled task. This has the effect of making it so that the
|
||||||
|
// OnAfterRenderAsync callbacks on components don't run by default.
|
||||||
|
// This way, by default prerendering gets the correct behavior and other renderers
|
||||||
|
// override the UpdateDisplayAsync method already, so those components can
|
||||||
|
// either complete a task when the client acknowledges the render, or return a canceled task
|
||||||
|
// when the renderer gets disposed.
|
||||||
|
|
||||||
|
// We believe that returning a canceled task is the right behavior as we expect that any class
|
||||||
|
// that subclasses this class to provide an implementation for a given rendering scenario respects
|
||||||
|
// the contract that OnAfterRender should only be called when the display has successfully been updated
|
||||||
|
// and the application is interactive. (Element and component references are populated and JavaScript interop
|
||||||
|
// is available).
|
||||||
|
return CanceledRenderTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -445,8 +445,12 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
||||||
{
|
{
|
||||||
if (updateDisplayTask.IsCanceled)
|
if (updateDisplayTask.IsCanceled)
|
||||||
{
|
{
|
||||||
// The display update was cancelled (maybe due to a timeout on the components server-side case or due
|
// The display update was canceled.
|
||||||
// to the renderer being disposed)
|
// This can be due to a timeout on the components server-side case, or the renderer being disposed.
|
||||||
|
|
||||||
|
// The latter case is normal during prerendering, as the render never fully completes (the display never
|
||||||
|
// gets updated, no references get populated and JavaScript interop is not available) and we simply discard
|
||||||
|
// the renderer after producing the prerendered content.
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
if (updateDisplayTask.IsFaulted)
|
if (updateDisplayTask.IsFaulted)
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,26 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
|
||||||
Assert.Equal("<p>Hello Steve!</p>", content);
|
Assert.Equal("<p>Hello Steve!</p>", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RenderComponent_DoesNotInvokeOnAfterRenderInComponent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = CreateHelper();
|
||||||
|
var writer = new StringWriter();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var state = new OnAfterRenderState();
|
||||||
|
var result = await helper.RenderComponentAsync<OnAfterRenderComponent>(new
|
||||||
|
{
|
||||||
|
State = state
|
||||||
|
});
|
||||||
|
result.WriteTo(writer, HtmlEncoder.Default);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("<p>Hello</p>", writer.ToString());
|
||||||
|
Assert.False(state.OnAfterRenderRan);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CanCatch_ComponentWithSynchronousException()
|
public async Task CanCatch_ComponentWithSynchronousException()
|
||||||
{
|
{
|
||||||
|
|
@ -309,6 +329,26 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class OnAfterRenderComponent : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter] public OnAfterRenderState State { get; set; }
|
||||||
|
|
||||||
|
protected override void OnAfterRender()
|
||||||
|
{
|
||||||
|
State.OnAfterRenderRan = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
|
{
|
||||||
|
builder.AddMarkupContent(0, "<p>Hello</p>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OnAfterRenderState
|
||||||
|
{
|
||||||
|
public bool OnAfterRenderRan { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
private class GreetingComponent : ComponentBase
|
private class GreetingComponent : ComponentBase
|
||||||
{
|
{
|
||||||
[Parameter] public string Name { get; set; }
|
[Parameter] public string Name { get; set; }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue