I am using NavigationManager.LocationChanged
to capture query strings. After getting the query string value I'm making an ajax call, which is async. LocationChanged itself is synchronous method, and it looks like there is no async version of LocationChanged
. And when calling async method from inside LocationChanged, the value set by the async method is lagging one step behind.
Here is the repro:
@page "/investigate"
@implements IDisposable
@inject NavigationManager NM
<h1>Sync: @SyncValue</h1>
<h1>Async: @AsyncValue</h1>
<button @onclick="TriggerLocationChange">Increment</button>
@code {
private string SyncValue;
private string AsyncValue;
private int Counter = 1;
protected override void OnInitialized()
{
NM.LocationChanged += OnLocationChanged;
}
public void Dispose()
{
NM.LocationChanged -= OnLocationChanged;
}
private void OnLocationChanged(object sender, LocationChangedEventArgs args)
{
// sync action, just for comparison
SyncValue = (Counter * 1000).ToString();
DoSomeAsync();
}
private async Task DoSomeAsync()
{
// http call to server
await Task.Delay(1);
AsyncValue = (Counter * 1000).ToString();
}
private void TriggerLocationChange()
{
Counter++;
NM.NavigateTo("investigate?counter=" + Counter);
}
}
The @AsyncValue
is lagging one step behind from the @SyncValue
.
How can I prevent the async method from lagging behind when called from inside LocationChanged?
After lots of trial and error, here is what I found:
Route parameter values are not yet set when LocationChanged runs. This is not shown in my example above but important in my case. Route parameter can be extracted manually from URL, or we can wait until blazor populates route parameter using await Task.Delay(1)
.
Call StateHasChanged()
at the end of the async method.
According to the documentation we should wrap the call with base.InvokeAsync(() => ...)
After these modifications, OnLocationChanged becomes:
private void OnLocationChanged(object sender, LocationChangedEventArgs args)
{
// ...
base.InvokeAsync(async () =>
{
await Task.Delay(1); // wait for blazor to populate route parameters
await DoSomeAsync();
StateHasChanged();
});
}
Marking a method with async does not change the signature of the method, hence you should be albe to do this:
private async void OnLocationChanged(object sender, LocationChangedEventArgs args)
{
// sync action:
SyncValue = (Counter * 1000).ToString();
await DoSomeAsync();
StateHasChanged();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With