【发布时间】:2020-03-26 08:30:40
【问题描述】:
在服务器端 Blazor 和 WebAssembly Blazor 项目的默认示例项目中,每次在页面之间移动时,Counter 示例都会重置为 0。但是,在 ASP.NET React 示例项目中,计数器不会在页面切换之间重置。
有没有办法让 Counter 之类的组件在 Blazor 中的页面导航之间保留状态(至少对于不进行任何服务器调用的 WebAssembly 项目而言)?
【问题讨论】:
在服务器端 Blazor 和 WebAssembly Blazor 项目的默认示例项目中,每次在页面之间移动时,Counter 示例都会重置为 0。但是,在 ASP.NET React 示例项目中,计数器不会在页面切换之间重置。
有没有办法让 Counter 之类的组件在 Blazor 中的页面导航之间保留状态(至少对于不进行任何服务器调用的 WebAssembly 项目而言)?
【问题讨论】:
https://docs.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-3.0#client-side-in-the-browser 中似乎讨论了这个确切的场景
似乎 Blazor 不能开箱即用地处理它,但您只需要使用 localStorage 或 sessionStorage。
使用Blazor.Extensions.Storage NuGet 包 (https://github.com/BlazorExtensions/Storage):
@page "/counter"
@inject ISessionStorage SessionStorage
@using Blazor.Extensions.Storage.Interfaces
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
protected override async Task OnInitializedAsync()
{
currentCount = await SessionStorage.GetItem<int>("counter");
}
private async void IncrementCount()
{
currentCount++;
await SessionStorage.SetItem<int>("counter", currentCount);
}
}
【讨论】:
我在我的文章中介绍了这一点(适用于服务器端 Blazor 以及客户端 (WebAssembly) Blazor):Implementing State Management In Blazor
使用以下代码添加一个名为 CounterState.cs 的类:
public class CounterState
{
public int CurrentCount { get; set; }
}
在 Startup.cs 中注册这个类,使用依赖注入:
services.AddScoped<CounterState>();
将以下代码添加到 .razor 代码页的顶部:
@inject CounterState CounterState
更改以下代码:
<p>Current count: @currentCount</p>
收件人:
<p>Current count: @CounterState.CurrentCount</p>
最后,将代码部分更改为以下内容:
@code {
void IncrementCount()
{
// ** SESSION STATE
int CurrentCount = CounterState.CurrentCount;
CurrentCount++;
// Set Current count on the Session State object
CounterState.CurrentCount = CurrentCount;
}
}
【讨论】:
对于服务器端 Blazor,如果您希望计数器值在所有选项卡/客户端上保持/更新,您只需将其存储在静态变量中并在值更改时重新呈现。
@page "/counter"
<h1>Static Counter</h1>
<p>Current static count: <span style="font-size:42px">@currentCount</span></p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
public static int currentCount = 0;
private void IncrementCount()
{
currentCount++;
StaticCount.FireUpdate();
}
protected override void OnInitialized()
{
base.OnInitialized();
StaticCount.NewCounterValue += OnNewCounterValue;
}
void OnNewCounterValue(object sender, EventArgs e)
{
InvokeAsync(() =>
{
StateHasChanged();
});
}
public void Dispose()
{
StaticCount.NewCounterValue -= OnNewCounterValue;
}
}
@code{
public static class StaticCount
{
public static event EventHandler NewCounterValue;
public static void FireUpdate()
{
NewCounterValue?.Invoke(null, new EventArgs());
}
}
}
如果您想在服务器重新启动之间保留该值,您也可以使用(单例)服务来执行此操作,甚至将值保存在数据库中。
【讨论】:
我写了一个关于 Blazor 状态管理的答案,在这里进行了调查:
https://stackoverflow.com/a/65609519/3850405
我的建议是内存状态容器服务。
例子:
StateContainer.cs
public class StateContainer
{
public string Property { get; set; } = "Initial value from StateContainer";
public event Action OnChange;
public void SetProperty(string value)
{
Property = value;
NotifyStateChanged();
}
private void NotifyStateChanged() => OnChange?.Invoke();
}
Program.Main(Blazor WebAssembly):
builder.Services.AddSingleton<StateContainer>();
Startup.ConfigureServices(Blazor 服务器):
services.AddSingleton<StateContainer>();
Pages/Component1.razor
@page "/Component1"
@inject StateContainer StateContainer
@implements IDisposable
<h1>Component 1</h1>
<p>Component 1 Property: <b>@StateContainer.Property</b></p>
<p>
<button @onclick="ChangePropertyValue">Change Property from Component 1</button>
</p>
<Component2 />
@code {
protected override void OnInitialized()
{
StateContainer.OnChange += StateHasChanged;
}
private void ChangePropertyValue()
{
StateContainer.SetProperty($"New value set in Component 1 {DateTime.Now}");
}
public void Dispose()
{
StateContainer.OnChange -= StateHasChanged;
}
}
Shared/Component2.razor
@inject StateContainer StateContainer
@implements IDisposable
<h2>Component 2</h2>
<p>Component 2 Property: <b>@StateContainer.Property</b></p>
<p>
<button @onclick="ChangePropertyValue">Change Property from Component 2</button>
</p>
@code {
protected override void OnInitialized()
{
StateContainer.OnChange += StateHasChanged;
}
private void ChangePropertyValue()
{
StateContainer.SetProperty($"New value set in Component 2 {DateTime.Now}");
}
public void Dispose()
{
StateContainer.OnChange -= StateHasChanged;
}
}
【讨论】: