Reputation: 47
I run into problem with multiple times called OnAfterRenderAsync(bool firstRender)
even when a previous run of the same method in the same component is not yet finished.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_jsModule = await JsRuntime.LoadJSModule(this);
await _jsModule!.InvokeVoidAsync("doLogic");
}
await _jsModule!.InvokeVoidAsync("doSomethingElse");
}
What the problem is:
OnAfterRenderAsync
runs first time with parameter firstRender = true
StateHasChanged
in parent componentAfterRenderAsync
is called second time with parameter firstRender = false
, even first run is still running and it's inside first condition_jsModule
is still null here. So it failsHow this situation should be handled correctly? I tried use Semaphore, and then it works fine. But I think that this situation should be handled somehow by Blazor itself?
I found this problem with one component till now, because I run StateHasChanged
in very short period of time.
I can imagine that it can happens also in other components and should be solved generally. Do you have some ideas or do I miss something?
Upvotes: 0
Views: 906
Reputation: 30410
OnAfterRender{Async}
is a UI Event. It's not an intrinsic part of the lifecycle. It's called after component renders. When it gets invokes is dependant on load on the SynchronisationContext
running the Blazor UI context.
You can assign the load method to a global variable and then await it like this:
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private Task<bool> _moduleLoad = Task.FromResult(true);
private void IncrementCount()
{
currentCount++;
}
protected override async Task OnParametersSetAsync()
{
await Task.Delay(1000);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Use this if you want to bail out if OnAfterRenderAsync is already running
if (!_moduleLoad.IsCompleted)
{
Console.WriteLine("OnAfterRenderAsync bailed out");
return;
}
if (firstRender)
_moduleLoad = DoSomethingSlow();
var x = await _moduleLoad;
var y = await DoSomethingElse();
}
private async Task<bool> DoSomethingSlow()
{
Console.WriteLine("DoSomethingSlow started");
await Task.Delay(2000);
Console.WriteLine("DoSomethingSlow completed");
return true;
}
private async Task<bool> DoSomethingElse()
{
Console.WriteLine("DoSomethingElse started");
await Task.Delay(10);
Console.WriteLine("DoSomethingElse completed");
return true;
}
}
What you get is:
DoSomethingSlow started
OnAfterRenderAsync bailed out
DoSomethingSlow completed
DoSomethingElse started
DoSomethingElse completed
Upvotes: 1