我什么时候应该调用StateHasChanged,什么时候Blazor会自动截获某些更改? [英] When should I call StateHasChanged and when Blazor automatically intercepts that something is changed?

查看:431
本文介绍了我什么时候应该调用StateHasChanged,什么时候Blazor会自动截获某些更改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难理解应该何时调用StateHasChanged(),何时Blazor截获某些更改,因此必须重新呈现.

I am having a hard time understanding when I should call StateHasChanged() and when Blazor intercepts that something is changed so it must be re-rendered.

我创建了一个带有按钮和名为AddItem的自定义组件的示例项目.该组件包含一个带红色边框和一个按钮的div.

I've created a sample project with a button and a custom component called AddItem. This component contains a div with a red border and a button.

我的期望:我希望当用户单击索引"页面中包含的按钮时,将显示AddItem的div.然后,我想在用户单击AddItem的按钮时将其隐藏.

What I expected: I want that the AddItem's div will show up when the user clicks on the button contained inside the Index page. Then I want to hides it when the user clicks on AddItem's button.

注意: AddItem不会在外部公开它的_isVisible标志,而是包含一个Show()方法.因此,当单击索引按钮时,将调用AddItems.Show().

Note: AddItem doesn't expose it _isVisible flag outside, instead it contains a Show() method. So AddItems.Show() will be invoked when the Index's button is clicked.

测试:

  1. 我单击Index的单击按钮,然后调用方法Open()AddItem.Show().标志_isVisible设置为true,但是什么也没有发生,并且索引的ShouldRender()被调用.

  1. I click on Index's click button then the methods Open() and AddItem.Show() are invoked. The flag _isVisible is set to true but nothing happens and Index's ShouldRender() is invoked.

控制台输出:

  • 渲染索引

我用public void Show() {_isVisible = true; StateHasChanged();}修改了AddItem.Show().现在,AddItem的div可以按预期显示和隐藏.

I've modified AddItem.Show() with public void Show() {_isVisible = true; StateHasChanged();}. Now the AddItem's div shows and hide as expected.

控制台输出:

  • 渲染AddItem(1°单击索引按钮)
  • 渲染索引(单击索引按钮1°)
  • 渲染AddItem(2°单击addItem的关闭按钮)

我用<AddItem @ref="AddItem" CloseEventCallback="CallBack" />修改了<AddItem @ref="AddItem" />,从AddItem的Show()方法中删除了StateHasChanged.现在,AddItem的div可以按预期显示和隐藏.

I've modified <AddItem @ref="AddItem" /> with <AddItem @ref="AddItem" CloseEventCallback="CallBack" />, removed StateHasChanged from AddItem's Show() method. Now the AddItem's div shows and hides as expected.

基于测试3:如果我将AddItem的CloseEventCallback设置为任何父级的方法,为什么不必显式地显示StateHasChanged?我很难理解它,因为AddItem不会在任何地方调用CloseEventCallback.

Based on Test 3: Why I don't have to explicit StateHasChanged if I set AddItem's CloseEventCallback to any parent's method? I'm having a hard time understanding it because AddItem doesn't invoke CloseEventCallback anywhere.

Blazor知道某些更改后,必须重新渲染吗?

When Blazor understands that something is changed so it must be re-render?

我的示例代码(如果您想尝试的话).

My sample code (if you want to try it).

我的Index.razor

My Index.razor

<AddItem @ref="AddItem" />
<button @onclick="Open">click</button>
@code {
    AddItem AddItem;

    public void Open()
    {
        AddItem.Show();
    }

    public void CallBack()
    {
    }

    protected override bool ShouldRender()
    {
        Console.WriteLine("Render INDEX");
        return base.ShouldRender();
    }
}

我的AddItem组件

My AddItem component

@if (_visible)
{
    <div style="width: 100px; height: 100px; border: 1px solid red">testo</div>
    <button @onclick="Close">close</button>    
}

@code {
    private bool _visible = false;

    [Parameter] public EventCallback<bool> CloseEventCallback { get; set; }

    public void Show()
    {
        _visible = true;
    }

    public void Close()
    {
        _visible = false;
    }

    protected override bool ShouldRender()
    {
        Console.WriteLine("Render ADDITEM");
        return base.ShouldRender();
    }
}

推荐答案

通常来说,触发UI事件后会自动调用StateHasChanged()方法, 例如,在单击按钮元素之后,将引发click事件,并且StateHasChanged()方法为 自动调用以通知组件其状态已更改,并且应重新呈现.

Generally speaking, the StateHasChanged() method is automatically called after a UI event is triggered, as for instance, after clicking a button element, the click event is raised, and the StateHasChanged() method is automatically called to notify the component that its state has changed and it should re-render.

首次访问索引组件时.父组件先渲染,然后渲染父组件 呈现其子项.

When the Index component is initially accessed. The parent component renders first, and then the parent component renders its child.

每当单击打开"按钮时,索引组件就会重新呈现(这是因为 事件的目标是父组件,默认情况下它将重新渲染 (无需使用StateHasChanged).但不是那个不知道自己的孩子 状态已经改变.为了使孩子知道他的状态已经改变并且状态已经改变 应该重新渲染,您应该添加对StateHasChanged方法的调用 手动在Show方法中.现在,当您单击打开"按钮时,子组件为 首先重新渲染,然后其父级重新渲染.现在红色的div变为可见.

Whenever the "Open" button is clicked the Index component re-renders (This is because the target of the event is the parent component, which by default will re-render (No need to use StateHasChanged). But not the child, who is not aware that his state has changed. In order to make the child aware that his state has changed and that it should re-render, you should add a call to the StateHasChanged method manually in the Show method. Now, when you click on the "Open" button, the child component is re-rendered first, and then its parent re-renders next. Now the red div is rendered visible.

点击关闭"按钮以隐藏红色的div.这次只有子组件重新呈现 (这是因为事件的目标是子组件,并且默认情况下会重新呈现), 但不是父母.

Click the "Close" button to hide the red div. This time only the child component re-renders (This is because the target of the event is the child component, and it re-renders by default), but not the parent.

此行为是正确的,是设计使然.

This behavior is correct and by design.

如果从AddItem.Show方法中删除对StateHasChanged方法的调用,请定义此 属性:[Parameter] public EventCallback<bool> CloseEventCallback { get; set; },并添加 父组件中的一个组件属性,可以为该属性分配一个值,如下所示: <AddItem @ref="AddItem" CloseEventCallback="CallBack" />,您会发现外观没有变化, 但这一次单击打开"按钮时的重新渲染顺序是父级 重新渲染,然后孩子重新渲染.这恰好描述了您发现的问题 您在评论中提出的问题:

If you remove the call to the StateHasChanged method from the AddItem.Show method, define this property: [Parameter] public EventCallback<bool> CloseEventCallback { get; set; }, and add a component attribute in the parent component to assign a value to this property like this: <AddItem @ref="AddItem" CloseEventCallback="CallBack" />, you'll notice no change outwardly, but this time the order of re-rendering when the "Open" button is clicked, is first the parent re-renders, then the child re-renders. This describes exactly the issue you've found expressed in your question from the comments:

那么,为什么即使未在任何地方调用CloseEventCallback,我的测试3仍能按预期工作?

So, why my test 3 worked as expected even if CloseEventCallback isn't invoked anywhere?

您是对的...在进行进一步调查之前,我无法真正解释此行为. 我将尝试找出正在发生的事情,并告诉您.

You are right... I could not really explain this behvior before having a further investigation. I'll try to find out what is going on, and let you know.

AddItem的close方法调用CloseEventCallback来通知父级应重新呈现.

AddItem's close method invoke the CloseEventCallback to advise the parent that it should re-render.

注意:您的代码使用布尔类型说明符定义CloseEventCallback,因此您必须定义一个方法 在具有布尔参数的父组件中.当您调用CloseEventCallback'delegate'时 您实际上调用了Index.Callback方法,并且应该向其传递一个布尔值.当然,如果您通过 一个组件的值,您希望它重新呈现,以便可以在UI中看到新状态.这是 EventCallback提供的功能:尽管事件是在子组件中触发的, 它的目标是父组件,这将导致父组件重新呈现.

Note: your code define the CloseEventCallback with a boolean type specifier, so you must define a method in your parent component that has a boolean parameter. When you invoke the CloseEventCallback 'delegate' you actually call the Index.Callback method and you should pass it a boolean value. Naturally, if you passes a value to a component, you expect it to re-render so that the new state can be seen in the UI. And this is the functionality that the EventCallback provides: Though the event is triggered in the child component, its target is the parent component, which results in the parent component re-rendering.

我想知道如果订阅的EventCallback之一被调用,为什么父组件应该重新呈现自身?

I am wondering why a parent component should re-render itself if one of the subscribed EventCallback is invoked?

这正是我在上一段中要解释的内容. EventCallback类型是专门为解决事件目标的问题而设计的,将事件路由到组件 状态更改(父组件),然后重新呈现.

This is exactly what I'm trying to explain in the paragraph above. The EventCallback type was especially design to solve the issue of the event target, routing the event to the component whose state has changed (the parent component), and re-rendering it.

这篇关于我什么时候应该调用StateHasChanged,什么时候Blazor会自动截获某些更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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