StateHasChanged()两次重新渲染一次组件 [英] StateHasChanged() re-render the component one time out of two

查看:212
本文介绍了StateHasChanged()两次重新渲染一次组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个Blazor服务器端项目,我想创建一个单击后便停用的按钮,但不使用<button>disabled属性.代码很简单:

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();
    }

}

出于奇怪的原因,第一个StateHasChanged不会触发,但是第二个会重新呈现页面.通过进入Debug模式并进入StateHasChanged()方法,可以很容易地进行测试.在第二次调用时,它会在进入方法后停止在HTML代码上,但不是第一次.

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.

为什么?

注意:我不是在寻找仅使用Task.DelayTask.Run(...)的解决方法,因为它存在于那些线程和UI刷新线程之间的竞争状态,因此并非如此.一个可靠的解决方案.我正在通过使用诸如PropertyChangedEventCallback之类的事件并将按钮作为子组件,来寻找StateHasChanged()行为或解决方法的答案.

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.

经过一些测试,看来StateHasChanged()仅在对Task进行await操作之后才触发组件的重新渲染.可以通过在result = await _LogInService.TryLogIn(logInForm);行中添加注释或将IsDisabled = ...更改为await new Task.Run(() => { IsDisabled = ...})来轻松对其进行测试.我现在有一些解决方法,但是我仍然想知道为什么这是一个功能. StateHasChanged()是否应该在执行任何操作后重新渲染?还是认为只有async操作(因此大部分是服务器调用)才能更改UI中的某些内容?

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 ?

推荐答案

以下是描述重新渲染如何发生的执行流程:

The following is the flow of execution to describe how re-rendering occurs:

  1. 父组件调用StateHasChanged
  2. 生成了新的渲染树
  3. 旧树与新树之间的差异正在发生
  4. 传递给您的子组件的值被认为与当前持有的值不同.
  5. 在子组件上调用
  6. SetParameters,以使用父级传递给它的值更新它们.
  1. Parent component calls StateHasChanged
  2. A new render tree is produced
  3. The diff between the old tree and the new tree is happening
  4. The values being passed to your child component are considered different to the ones it currently hold.
  5. SetParameters is being called on the child component to update them with the values the parent passed to it.

现在,在将值分配给局部变量IsDisabled之后调用StateHasChanged时,并不会真正更改组件的状态,因此没有理由调用StateHasChanged会产生重新渲染.当您调用StateHasChanged时,它只是将该组件的渲染请求排队.但是没有理由重新渲染...

Now, when you call StateHasChanged after assigning a value to the local variable IsDisabled, does not really change the state of the component, and there is no reason why calling StateHasChanged will yield a re-rendering. When you call StateHasChanged, it simply queues a render request for that component. But there is no reason for re-rendering...

或者它认为只有异步操作(因此大部分是服务器调用)才能 更改用户界面中的某些内容?

Or it consider that only async operations (so mostly server-calls) can change something in the UI ?

除了OnInitializedAsync方法外,操作的类型是否异步均无关紧要,在这种情况下,当OnInitializedAsync方法完成以再次重新呈现UI时,将自动调用StateHasChanged方法.异步调用检索到的新数据将在OnInitializedAsync方法中执行.

It has nothing to do whether the type of the operation is async or not, except of the OnInitializedAsync method, in which case, the StateHasChanged method is automatically called when the OnInitializedAsync method completes to re-render the UI once again, with the new data retrieved by the async calls perform in the OnInitializedAsync method.

更新:

您想要的东西可以通过多种方式完成,其中最简单的方法在这里得到展示:

What you want can be done in various ways, the simplest of which is demonstrated here:

   <input type="button" value="Click me now"  disabled="@IsDisabled" @onclick="TryLogIn" />


@code{ 

    bool IsDisabled;

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

    async Task TryLogIn()
    {
        IsDisabled = true;

        // Do some async work here...
        // Note: Replace your async method with Task.Delay 
        await Task.Delay(5000);

        IsDisabled = false;

    }

}

这应该有效... 注意:禁用按钮控件的唯一方法是使用disabled属性

This should work... Note: The only way you can disable your button control is by using the disabled property

无需调用StateHasChanged方法.当编译器(编译器)为您的组件创建EventCallback委托"时,编译器插入您的源代码中的代码会自动调用它.

No need to call the StateHasChanged method. It is automatically called by the code the compiler insert into your source code when it (the compiler) creates an EventCallback 'delegate' for your component.

触发UI事件后,将自动调用StateHasChanged方法.

The StateHasChanged method is automatically called after a UI event is triggered.

这篇关于StateHasChanged()两次重新渲染一次组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆