【问题标题】:How to make a razor component re-render from another razor component如何使剃刀组件从另一个剃刀组件重新渲染
【发布时间】:2021-01-27 09:25:50
【问题描述】:

我似乎无法从另一个组件的操作中重新渲染剃须刀组件。例如:

假设我们有一个组件库“ComponentVariables”,其中定义了一个变量“buttonClickCount”来跟踪按钮被点击的次数,以及增加变量的方法。

组件变量.cs

sing Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;

namespace WebApplication2.Components
{
    public class ComponentVariables : ComponentBase
    {
        [Parameter]
        public int buttonClickCount { get; set; }

        public ComponentVariables() { }

        protected override void OnInitialized()
        {
            buttonClickCount = 0;
        }

        public void clickCounter()
        {
            buttonClickCount++;
            StateHasChanged();
        }
    }
}

然后它被 _Imports.razor 中的所有组件以@inherits ComponentVariables 继承

home组件有如下代码

Home.razor

<div class="row">
    <strong>@buttonClickCount</strong>
    <Clicker/>
</div>

然后在 Clicker.razor 组件中创建按钮。以及引用组件库中方法的 onclick。和一个包含计数的 div(也来自组件库)。

Clicker.razor

<div>
    <button @onclick="clickCounter">Click Me</button>
</div>

<div>@buttonClickCount</div>

@code
{
}

就像现在一样,home.razor 和 clicker.razor 都在 ComponentVariables.cs 中使用相同的变量来显示一个值。但是,当我单击时,只有 Clicker 组件会重新呈现。即 home 始终显示 0,而 clicker 会随着您的点击而增加。

对此的任何帮助将不胜感激。

【问题讨论】:

  • 你使用了继承。因此,看起来您操作相同的变量是因为它们具有名称,但实际上,它们都是具有唯一值的唯一实例。也许,你可以描述一下你想用这种方法实现什么?
  • 我开始更多地使用调试器,并注意到每个剃须刀组件都调用了基础。这意味着我怀疑你是对的。那么在这种情况下,我将如何在所有组件中使用引用相同的变量,并使它们都随着对所述变量的更新而动态变化?
  • 有三种处理方式。根据您的描述,我会推荐一个 AppState。我已经写了一个答案here。或者你可以看看this post
  • 非常感谢!我刚刚通读了这两个链接。它们看起来很有帮助,尤其是 appstate 之一。不过,后续问题是否可以根据更改的变量将重新渲染限制为特定组件?因为这对我的情况来说是最重要的部分。
  • @VictoryObot,事件或代表呢? &lt;Clicker onClick="InvokeAsync(StateHasChanged)" /&gt;

标签: asp.net-core razor blazor


【解决方案1】:

您使用了继承。因此,看起来您操作相同的变量是因为它们具有名称,但实际上它们都是具有唯一值的唯一实例。

如果你想在不同的组件之间交换一个值,有三种方法可以做到。 This blog post 描述它们。根据您的描述,我建议使用 AppState 方法。我写了一个答案here

我已经复制了我之前答案的相关部分并添加了一些新想法。

让我们从定义AppState 开始。此类使用 .NET 事件或委托在值发生更改时发出通知。

public class AppState
{
    private Int32 _clickCounter = 0;

    public Int32 ClickCounter
    {
        get => _clickCounter;
        set
        {
            _clickCounter = value;
            AppStateChanged?.Invoke(nameof(ClickCounter), this);
        }
    }

    public event AppStateChangedHandler AppStateChanged;
}

处理程序public delegate void AppStateChangedHandler(String propertyName, AppState state);。更改处理程序包括对象本身和已更改的propertyName。订阅事件的组件可以使用它来过滤更改是否相关。

AppState 被添加到依赖注入系统中。对于 Blazor WASM,它应该是一个单例依赖项。但是,对于 Blazor Server,它应该是一个作用域。

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    builder.RootComponents.Add<App>("#app");

    ...
    builder.Services.AddSingleton(new AppState());
    ... 
   
    await builder.Build().RunAsync();
}

默认模板中的Counter 组件应该是使用 AppState 传播更改的示例。该组件略有改动。

@page "/Counter"
@inject AppState appState

<h1>Counter</h1>

<p>Current count: @appState.ClickCounter</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {

    private void IncrementCount()
    {
        appState.ClickCounter++;
    }
}

使用 AppState 中的相应属性,而不是递增(和读取)局部变量。

为了简化对更改事件的订阅并减少每个组件中的样板,我们创建了一个名为 AppStateAwareComponentBase 的抽象基础。这应该是一个普通的 .cs 文件。

public abstract class AppStateAwareComponentBase : ComponentBase, IDisposable
{
    [Inject]
    protected AppState AppState { get; private set; }

    protected override void OnInitialized()
    {
        base.OnInitialized();

        AppState.AppStateChanged += AppStateChanged;
    }

    protected abstract Boolean HandleAppStateChanged(String propertyName, AppState state);

    private async void AppStateChanged(String propertyName, AppState state)
    {
        Boolean changesOccured = HandleAppStateChanged(propertyName, state);
        if (changesOccured == true)
        {
            await InvokeAsync(StateHasChanged);
        }
    }

    public void Dispose()
    {
        AppState.AppStateChanged -= AppStateChanged;
    }
}

此类订阅了更改事件,但不知道如何准确处理它。因此,它有一个名为HandleAppStateChanged 的抽象方法,需要稍后在具体类中实现。此类确定布局更改是否是后续结果。如果需要更新布局,子调用将返回 true。父类 (AppStateAwareComponentBase) 通过调用 InvokeAsync(StateHasChanged) 来启动布局更新。

如果AppState 是通过[Inject] 注入的,并且IDisposable 的实现保证在从树中移除组件时移除对委托的订阅。

我创建了一个CounterHeader 组件,作为一个继承自 AppStateAwareComponentBase 的类

@inherits AppStateAwareComponentBase

<span>Current App Global Counter: @_counter </span>

@code{

   private Int32 _counter = 0;

   protected override Boolean HandleAppStateChanged(String propertyName, AppState state)
   {
       if(propertyName != nameof(AppState.ClickCounter)) {
           return false;
       }
       
       _counter = state.ClickCounter;
       return true;
   }
}

Here is a link to the corresponding repository.

其他想法

这是“AppState”的基本版本。但是,对于所有这些状态转换,都有像 Fluxor 这样的框架,或者您可以使用更解耦的方式,例如组件的消息总线,称为 ComponentBus

每个应用程序可以有多个“状态”,例如ShoopingCardStateUserState 等,以将其拆分为不同的较小部分。

【讨论】:

  • 不敢相信我忘了说谢谢。所以,非常感谢你fr
猜你喜欢
  • 2020-02-19
  • 2017-04-16
  • 1970-01-01
  • 2022-11-23
  • 1970-01-01
  • 2016-12-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多