55using System . Collections . Generic ;
66using System . Diagnostics ;
77using System . Runtime . ExceptionServices ;
8+ using System . Threading ;
89using System . Threading . Tasks ;
910using Microsoft . AspNetCore . Components . RenderTree ;
1011using Microsoft . Extensions . Logging ;
@@ -21,6 +22,8 @@ public class HtmlRenderer : Renderer
2122 "area" , "base" , "br" , "col" , "embed" , "hr" , "img" , "input" , "link" , "meta" , "param" , "source" , "track" , "wbr"
2223 } ;
2324
25+ private static readonly Task CanceledRenderTask = Task . FromCanceled ( new CancellationToken ( canceled : true ) ) ;
26+
2427 private readonly Func < string , string > _htmlEncoder ;
2528
2629 /// <summary>
@@ -40,7 +43,19 @@ public HtmlRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFacto
4043 /// <inheritdoc />
4144 protected override Task UpdateDisplayAsync ( in RenderBatch renderBatch )
4245 {
43- return Task . CompletedTask ;
46+ // By default we return a canceled task. This has the effect of making it so that the
47+ // OnAfterRenderAsync callbacks on components don't run by default.
48+ // This way, by default prerendering gets the correct behavior and other renderers
49+ // override the UpdateDisplayAsync method already, so those components can
50+ // either complete a task when the client acknowledges the render, or return a canceled task
51+ // when the renderer gets disposed.
52+
53+ // We believe that returning a canceled task is the right behavior as we expect that any class
54+ // that subclasses this class to provide an implementation for a given rendering scenario respects
55+ // the contract that OnAfterRender should only be called when the display has successfully been updated
56+ // and the application is interactive. (Element and component references are populated and JavaScript interop
57+ // is available).
58+ return CanceledRenderTask ;
4459 }
4560
4661 /// <summary>
0 commit comments