【问题标题】:Can the state of the Counter in the example Blazor project be preserved between page switches?示例 Blazor 项目中 Counter 的状态是否可以在页面切换之间保留?
【发布时间】:2020-03-26 08:30:40
【问题描述】:

在服务器端 Blazor 和 WebAssembly Blazor 项目的默认示例项目中,每次在页面之间移动时,Counter 示例都会重置为 0。但是,在 ASP.NET React 示例项目中,计数器不会在页面切换之间重置。

有没有办法让 Counter 之类的组件在 Blazor 中的页面导航之间保留状态(至少对于不进行任何服务器调用的 WebAssembly 项目而言)?

【问题讨论】:

    标签: c# blazor


    【解决方案1】:

    https://docs.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-3.0#client-side-in-the-browser 中似乎讨论了这个确切的场景

    似乎 Blazor 不能开箱即用地处理它,但您只需要使用 localStoragesessionStorage

    使用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);
        }
    }
    

    【讨论】:

      【解决方案2】:

      我在我的文章中介绍了这一点(适用于服务器端 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;
          }
      }
      

      【讨论】:

      • 请注意,Scoped 几乎类似于 Session,但此 counterState 无法在 SignalR 重新连接后继续存在。对于某些场景来说已经足够了,但 localState 是一种更安全的解决方案。
      • @Henk Holterman - 在大多数情况下,您希望跟踪“会话状态”,并且您希望所有与当前会话无关的值(例如最近选择的“类别”)都“消失” ' 如果会话已卸载,否则用户再次登录并且您尝试加载不再有效的“类别”。此外,如果您有“敏感”信息,例如“帐户 ID”,您不希望将它们存储在用户的本地 Web 浏览器中。您想将它安全地存储在内存中,并且希望它从任何地方完全消失 :)
      • 是的,但我认为 localStore 比 SignalR 上下文更“会话”。
      • 请查看我对通用服务的实现,让我知道您对它作为通用解决方案的看法stackoverflow.com/questions/65044349/…
      • @MichaelWashington 随意撕下它并将其作为示例添加到您的网站上。 github.com/rdavi10471a2/BlazorApp1
      【解决方案3】:

      对于服务器端 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());
              }
          }
      }
      

      如果您想在服务器重新启动之间保留该值,您也可以使用(单例)服务来执行此操作,甚至将值保存在数据库中。

      【讨论】:

      • 你能提供一些描述你正在使用的方法的细节吗? OP 询问了服务器端和 Web 程序集(客户端),而不仅仅是服务器端。我还觉得 OP 只是询问在模板应用程序(例如天气和计数器)之间移动并保留计数器而不是在多个选项卡之间保留。
      • 对于服务器端 Blazor.. "StateHasChanged", "ASP.NET Core Blazor 生命周期"
      【解决方案4】:

      我写了一个关于 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;
          }
      }
      

      https://docs.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-5.0&pivots=webassembly#in-memory-state-container-service-wasm

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-11
        • 2019-02-22
        • 1970-01-01
        • 1970-01-01
        • 2019-03-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多