-
Notifications
You must be signed in to change notification settings - Fork 10.1k
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
Blazor - OnRenderAsync being fired twice with PreRendering enabled #11876
Comments
Thank you for filing this issue. In order for us to investigate this issue, please provide a minimalistic repro project that illustrates the problem. |
@phinett do you have a minimal repro project we can use to further investigate this? |
I'm not the author of this issue but I have just made two tests: one with default template for Blazor server side application and one with Blazor client side with prerendering enabled (see this repo: https://github.com/danroth27/ClientSideBlazorWithPrerendering). In both tests I have this in @page "/"
@inject IComponentContext ComponentContext
<h1>Hello, world!</h1>
Welcome to your new app. @code
{
static int counter = 0;
protected override Task OnAfterRenderAsync()
{
string prerendering = ComponentContext.IsConnected ? "rendered in the browser" : "prerendering";
Console.WriteLine($"OnAfterRender: {++counter} {prerendering}");
return base.OnAfterRenderAsync();
}
} Compile the application and hit Ctrl+F5 to run it without debugger. In sever side version I see only one message from my app: "OnAfterRender: 1 rendered in the browser". I see it in VS output window. It looks that OnAfterRenderAsync is called only once and only after browser connects to the server. This is expected. In client side version I see this message in VS output window "OnAfterRender: 1 prerendering" and then this message in browser's console "WASM: OnAfterRender: 1 rendered in the browser". It looks that in client side version OnAfterRender is really called twice: once on the server when page is prerendered and second time when page is rendered in the browser. To be consistent with server side version OnAfterRender should be called only once and only in the browser. |
It's called twice because there are two renders instead of one, it's consistent. There is no magic happening here. The server prerenders the app (through what we call the "static prerenderer" and then tears it down. Then the client app boots up and renders the original app again (hence 2 renders 2 calls). On the prerender+reconnect case (the original server side example) we delay invoking onafterrender until we have established a connection to the server, to provide an experience similar to what happens in the non prerendered case. We could if we wanted to, never trigger the onafterrender event during static rendering, but that's it. @SteveSandersonMS do you have any thoughts on that? I don't see a big issue given that we dispose the renderer after it. |
In the doc we can read:
The question is: do we need OnAfterRender events for something else? If not, it shouldn't be called during prerendering because DOM does not exist at this time. Now in OnAfterRender programmers have to manually test this condition. |
That's what I thought we were doing. I'm slightly surprised we do call onafterrender in static rendering. Given that there's virtually nothing useful you can do after a static render has completed (the HTML output is already determined, and if you want teardown logic, use @danroth27 @rynowak Do you agree? |
Sounds good to me. Implementation wise, we can simply return a cancelled task to avoid doing any onafterrender work. We can always think of static prerendering as a render that completes partially. |
I have managed to create a small repo of the issue (took some time to narrow down the issue!) If i have any call in OnInitAsync which does any async/await work, it causes the OnAfterRenderAsync function to be called twice for some unknown reason. Hope this helps. |
@phinett Your repro doesn't repro, but I have an idea why you are seeing it called twice. When the browser (specially chrome) autocompletes, starts requesting the page before you even "commit" to the page so sometimes it ends up with two requests for the index page, resulting in two after render calls. |
It definetly is called twice for me, this is on initial page load from when i start debugging. I have tried on MS Edge as well and it reproduces every time. |
@phinett Not sure what to tell you, with the sample you uploaded, its server-side blazor and we have tests for this. One thing you can try is to use an InPrivate/Incognito window and see if you observe it there. The other thing that might be happening (based on your sample) is that you have an async OnInit, so there are going to be two renders and the onafterrender method is going to be called once for each render. Remove your async OnInit and see that you can't repro it. (I did that because the code was not compiling). |
I would expect it to be called twice, because there would be two renders. The first would be synchronously when your |
So how can i call JsInterop to register my client side JS plugin only once? I have tried everything and i can't find a way. I have even tried adding a boolean to do a check such as bool hasAlreadyPreRendered which is set to true after running, but the second time its called it's still set to false. |
Does your repro show that happening? @javiercn, were you able to see that? I looked at the repro code and couldn't see any flag such as this. |
I have attached a modified repo, i really hope you can see it reproducing this behavior. Thanks for taking the time to look into this! |
I took another look. This is again expected behavior, and the reason is that you have an oninitasync method that produces two render batches.
That's why you see two calls to onafterrenderasync |
@javiercn , @SteveSandersonMS This issue was closed but in response to my example code #11876 (comment) Steve wrote:
Is it fixed? Is there any other issue to track this problem? |
Preview #8 It's not calling the methods twice on "a" component. It actually creates two components that each have their events fired. But the prerender one fails to get dotnet references. Remove Html.RenderStaticComponentAsync and the problem goes away. The bigger problem is that OnAfterRenderAsync is firing on the prerendering (8 3.0.100-preview8-013656) dotnet new blazorserver add to Index.razor Below is the output (Hello there appears twice) info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] Hello Thereinfo: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[4] Hello There |
This was already fixed as part of preview9 |
When i have PreRendering enabled on my blazor app, i am getting 2 calls to OnAfterRenderAsync for my page.
I am trying to call some JS interop in this event to initialise a javascript plugin, but its getting called twice causing some issues.
I have tried creating a bool flag and doing a if(alreadyPreRendered) around my JsInterop call but the bool is always reset on the second time around.
Disabling PreRendering fixes the issue (it still calls the event twice, but my bool flag now works).
The text was updated successfully, but these errors were encountered: