问题
I have a Blazor app, with mulitple pages and components etc. I need to persist & pass a global object between pages and components. Say for example, a CUSTOMER object that will be read and updated between pages. What is the best way to do this? Use SessionStorage?
https://docs.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-3.1
Or use a Service like this? StateManager?
https://blog.jeremylikness.com/blog/blazor-state-management/
Thoughts? Thx in advance
回答1:
The first article you link to is what you would do if you're building a Blazor app with the server-side model. It doesn't give you anything of use if you're using a Blazor client-side (WebAssembly, WASM) model.
The second article you link to contrasts approaches between Blazor Server and Blazor WASM.
So, if you're using Blazor WebAssembly, you should us a service like Jeremy Likeness' StateManager (unless you want to code one yourself!), but if you are using Blazor Server-side, you don't need one, you can just use the advice in that Microsoft article you posted.
回答2:
In my experience, services are so easy to set up and use that I'd use them. If you're using server side, be careful which option you use for scope:
SCOPED will allow you to pass data among pages, but not if a new session is started (for example by hitting Refresh).
SINGLETON will persist across sessions, including those of other users. If you use singleton for anything other than global utilities, make sure you have some mechanism for validating the user.
回答3:
I think the link you have posted sums it up quite well. These are the common locations for persisting state:
- Server-side storage
- URL
- Browser storage
- In-memory state container service
You can read about them individually in the links. Regarding sessionStorage
Microsoft notes:
localStorage and sessionStorage can be used in Blazor WebAssembly apps but only by writing custom code or using a third-party package.
With the following warning:
Users may view or tamper with the data stored in localStorage and sessionStorage.
An object does not fit to be passed around in the URL and from your question it does not seem that you want to get the object from server-side storage every time.
Therefore my recommendation would be in-memory state container service.
Example:
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 Server):
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;
}
}
You can also use bind across more than two components and through any number of nested components, but you must respect the one-way flow of data:
- Change notifications flow up the hierarchy.
- New parameter values flow down the hierarchy.
ParentComponent.razor
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<ChildComponent @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
{
parentMessage = $"Set in Parent {DateTime.Now}";
}
}
ChildComponent.razor:
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<GrandchildComponent @bind-GrandchildMessage="BoundValue" />
</div>
@code {
[Parameter]
public string ChildMessage { get; set; }
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
private string BoundValue
{
get => ChildMessage;
set => ChildMessageChanged.InvokeAsync(value);
}
private async Task ChangeValue()
{
await ChildMessageChanged.InvokeAsync(
$"Set in Child {DateTime.Now}");
}
}
GrandchildComponent.razor
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue()
{
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
}
https://docs.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-5.0#bind-across-more-than-two-components
来源:https://stackoverflow.com/questions/61120899/blazor-global-objects-use-sessionstorage