针对特定组件使用 StateHasChanged 而非重新渲染页面上的所有组件

6

我在我的应用程序中使用了多个组件,每个组件都以条件方式呈现。有没有可能仅重新呈现特定的组件?

MyRazor.razor 文件,

 <button @onclick="Chnage">Second</button>
<div>
    @if (renderOne)
    {
        var data1 = Count++;
        <ComponentFirst>@data1</ComponentFirst>
    }
    @if (renderTwo)
    {
        var data2 = Count2++;
        <ComponentSecond class="btn-danger">@data2</ComponentSecond>
     }
</div>
@code { 
    void Chnage()
    {
        renderOne = true;
    }
}

通过分别检查相应的布尔值来渲染ComponentFirst和ComponentSecond。在按钮点击中,我只启用了ComponentFirst,但是ComponentSecond也再次渲染。我的目标是,如果我启用renderOne,则仅应重新渲染ComponentFirst。如果我禁用renderTwo,则应该仅渲染ComponentTwo而不是在应用程序的单个更改中同时渲染两个组件。

2个回答

3

你可能不需要担心组件多次渲染。渲染只会构建一个渲染树,不会更新浏览器的DOM。只有整个页面的渲染树构建完成后,Blazor才会将其与上一次的渲染树进行比较,并从差异中更新DOM。

话虽如此:

如果父组件通过[Parameter]属性(包括RenderFragment)向子组件传递任何信息,则每当父组件重新渲染时,子组件也会重新渲染,以防出现任何更改。

如果您希望组件独立于其父级重新渲染,则不应将任何状态传递给它们。您可以通过在组件外部存储状态来实现这一点。

public class MyState
{
  public int Count1 { get; set; }
  public int Count2 { get; set; }

  public event EventHandler<EventArgs> Changed;
  public void NotifyChanged() => Changed?.Invoke(this, EventArgs.Empty);
}

如果您将其注册为可注入的依赖项,那么您可以直接在子组件中使用它。
@inject MyState State
@implements IDisposable

<div>
  The value is @State.Count1
</div>

@code
{
  protected override void OnInitialized()
  {
    State.Changed += DoUpdate;
  }

  void IDisposable.Dispose()
  {
    State.Changed -= DoUpdate; // Important1
  }

  private void DoUpdate(object sender, EventArgs e)
  {
    InvokeAsync(StateHasChanged);
  }
}


任何组件都可以通过注入MyState并执行以下操作来更新状态:
injectedState.Counter1++;
injectedState.Counter2--;
injectedState.NotifyChanged();

1

关于Peter Morris的回答中直接向子组件传递状态的相关内容,我的做法是创建一个通用类,它将一个对象包装在一个带有OnChange事件的属性中,在setter中调用该事件:

public class Subscribable<T>
{
    public Subscribable() { }
    public Subscribable(T initialValue) { _value = initialValue; }

    private T? _value;

    public T? Value
    {
        get => _value;
        set
        {
            _value = value;
            OnChange?.Invoke();
        }
    }

    public event Action? OnChange;
}

然后,您可以将任何对象与注入的 MyState 类包装在这个 Subscribable 类中:
public class MyState
{
    public Subscribable<int> Count1 { get; } = new(0);
    public Subscribable<int> Count2 { get; } = new(0);
    public int Count3 { get; set; }
}

当你将MyState注入到子组件中时,你可以通过在该对象的OnChange事件中注册StateHasChanged来告诉组件在任何Subscribable对象更改时重新渲染。
@inject MyState State
@implements IDisposable

<p>
  @State.Count1.Value
  <br />
  This component will re-render when Count1 changes.
</p>
<p>
  @State.Count2.Value
  <br />
  This component will not re-render when Count2 changes, because
  StateHasChanged was not registered with the OnChange event for Count2.
</p>
<p>
  @State.Count3
  <br />
  When Count3 changes, nothing will re-render anywhere, because there is no 
  mechanism defined to tell any component that Count3 has changed.
</p>

@code
{
    protected override void OnInitialized()
    {
        State.Count1.OnChange += StateHasChanged;
    }

    void IDisposable.Dispose()
    {
        State.Count1.OnChange -= StateHasChanged;
    }
}

你可以在注入状态的属性级别上紧密控制呈现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接