I am trying to figure out when I need to call StateHasChanged(). I know when changing the value via Javascript, and blazor component may need to be forced to render the page. In the following code, I have a method called HandleClick to handle the button click and I intentionally commentted out StateHasChanged() . The HandleClick method itself calls Javascript to set the value of element in Part 2. Since Blazor component is not aware of the change of the value for fromJs, so the value in Part 1 is not updated after the click. But the question is why the value in Part 1 gets changed when I click the button again.
@page "/ExampleY"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
<div class="p-2">
<p class="alert-info p-3">
Part 1. Get value via JS call: <strong>@(fromJs ?? "Empty")</strong>
</p>
<p class="alert-success p-3">
Part 2. Set value via JS call: <strong @ref="element"></strong>
</p>
<button class="btn btn-success" @onclick="HandleClick">Click to Call JS</button>
</div>
@code {
private string fromJs;
private ElementReference element;
public async void HandleClick()
{
fromJs = await JSRuntime.InvokeAsync<string>("setText", element, "Hello from JS call!");
// StateHasChanged();
}
}
2
Answers
This has nothing to do with JavaScript Interop.
When you make this change:
then both parts will update at the same time.
Almost always avoid
async void
. It is a last resort for old-style eventhandlers like Timer.Elapsed. Blazor events can return eithervoid
or[async] Task
.A ButtonClick will always cause a re-render. But because of the
async void
this can happen beforefromJs
is assigned. The await inawait JSRuntime.InvokeAsync(...)
allows that. On the next click you will see the previous value of fromJs appear in Part 1.Use
"Hello from JS call! "+DateTime.Now
to see this versioning happen.The Blazor "internal" event handler code looks like this:
If you pass task a void as in:
there’s nothing to wait on.
task
is completed and the final render event never takes place. That’s why anyasync
handler (i.e. a method with an await), as the one above, must return a Task.So to answer your questions. The value in Part 1 is updated, but after the re-render of the component – no Task to wait on. When you click the button again you show the "last" value, i.e. the one from the last button click, not the current click. If you set the text to the click time you would see this.