Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StateHasChanged() re-render the component one time out of two

I'm making a Blazor Server Side project and I wanted to make a button that desactivate after a click, but without using the disabled attribute of <button>. The code is pretty simple :

@functions {

    LogInForm logInForm = new LogInForm();
    bool IsDisabled;
    SignInResult result;

    protected override void OnInitialized()
    {
        IsDisabled = false;
    }

    async Task TryLogIn()
    {
        IsDisabled = true;
        StateHasChanged();
        result =  await _LogInService.TryLogIn(logInForm);
        Console.WriteLine("Logging status : " + (result.Succeeded ? "Sucess" : "Failure"));
        IsDisabled = false;
        StateHasChanged();
    }

}

For odd reasons, the first StateHasChanged isn't triggering but the second does re-render the page. It can be quite easily testes by going in Debug mode and entering into the StateHasChanged() method. On second time call it does stop on the HTML code after going into the method but not the first time.

Why so ?

NB : I'm not looking for any workaround using Task.Delay or Task.Run(...) only, as it exist a race condition between those threads and the UI refresh thread, and so it is not a reliable solution. I'm looking for answers on the StateHasChanged() behaviour or a workaround by using events like PropertyChanged or EventCallback and putting the button as a child component.

Edit : After some testing, it seems that StateHasChanged() only triggers the re-render of the component after an await operation on a Task. It can be easily tested by putting in commentary the result = await _LogInService.TryLogIn(logInForm); line or changing IsDisabled = ... to await new Task.Run(() => { IsDisabled = ...}). I have some workaround now, but I still wonder why this is a feature. Shouldn't StateHasChanged() re-render after any operations ? Or it consider that only async operations (so mostly server-calls) can change something in the UI ?

like image 519
PepperTiger Avatar asked Dec 23 '22 20:12

PepperTiger


1 Answers

The Blazor team is about to publish documentation about how StateHasChanged() works.

You can track it here here: https://github.com/aspnet/AspNetCore/issues/14591

For the time being, I think this explanation taken from a github comment is an excellent explanation:

Adding a call to StateHasChanged simply queues the component to be rendered. The renderer decides when the renders happen.

This can be triggered by 4 circumstances:

  • Initial render where the bootstrap process triggers the initial render of the root component and all its children.
  • An event, in which the component that handles the event automatically triggers a new render after the event, and potentially its children if it renders new children or change their parameters.
  • As a result of calling StateHasChanged from an InvokeAsync call (marshalling back into the UI thread, essentially)
  • As a result of the parent component changing the parameters for the child component, which happens as part of the diffing process when the renderer calls SetParametersAsync on the child component.

To be very clear, calling StateHasChanged only queues a Render for the component or "marks it as dirty".

It's the renderer the one that decides when and how to produce the renders. BuildRenderTree does not result in new rendered output, only in a new definition of the "V-DOM" for the component at the time it's being called.

Normally, a component gets rendered once per render batch (which is a collection of components that are rendered/diffed together and sent to the UI for update). There are only two situations in which a component renders more than once in a batch:

  • You have a component that directly implements IComponent and calls RenderHandle.Render
  • You have a circular dependency between a child and a parent component that might cause a parent to re-render as part of a children invoking some callback parameter from the parent as part of its initialization

Source: https://github.com/aspnet/AspNetCore/issues/15175#issuecomment-544890549

like image 169
Postlagerkarte Avatar answered Mar 24 '23 22:03

Postlagerkarte