-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support persistent component state across enhanced page navigations #51584
Comments
Thanks for contacting us. We're moving this issue to the |
This could lead to developers (at least me) making some mistakes about assuming which branch of the TryTakeFromJson condition is run at which point. In my app I initialized a SignalR connection in the true branch (cause I thought it would always run on the WASM client side), and was confused when the connection was sometimes initialized and sometimes not. This also led to double database queries when navigating inside the app to the WASM page. Hopefully this is fixed ASAP. |
I agree. Not fixing this for a LTS release is a mistake IMO. |
Has anyone come up with a workaround for this yet? The intense flickering on page load when navigating is a truly awful UX. |
In case anyone is interested. I have created a repo both demonstrating the problem and implementing a workaround. If anyone improves on that please let me know: https://github.com/oliverw/BlazorCustomApplicationStateApp I should add that this is a Band-Aid at best and not a particularly good one:
|
I managed to solve my issue by moving from full WASM interactivity page to using a server rendered page, and having interactive elements as WASM components, to which I pass the required state as parameters. It removes any client side data fetching needs, usage of PersistComponentState and therefore the page flickering. Don't know why I hadn't tried passing state from a server rendered component to a WASM component previously (too stuck on previous hosted WASM model I guess), now I'm just wondering how on earth is the state actually persisted when passing parameters to WASM from server... (Edit: I see that the parameter state is deserialized into the prerendered html, makes sense. Still I hope that the PersistComponentState would work more consistently, having all sorts of random bugs with it when using it in some places) |
Thanks for contacting us. We're moving this issue to the |
@javiercn ... Due to the importance of this subject, I'm going to place a cross-link to this issue for further dev discussion, particularly so devs can find a workaround approach. If you have a workaround approach that the topic can describe and will be supported, then we can add it to the topic and drop the cross-link to this issue. I'm 👂 if you want to sanction something. If not, we can leave the cross-link in place until .NET 9 lands. |
@Markz878's workaround is working pretty well. |
Is there any reason to keep the I did build off of your example to try to reduce the boilerplate required. Here's what I ended up with: app.jsfunction getInnerText(id) {
return document.getElementById(id).innerText;
} PersistentComponent.cspublic abstract class PersistentComponentBase<TState> : ComponentBase
where TState : new()
{
[Inject]
private IJSRuntime Js { get; set; } = default!;
protected TState State { get; } = new();
protected abstract string StateKey { get; }
protected override void BuildRenderTree(RenderTreeBuilder builder) =>
builder.AddMarkupContent(1, Serialize());
protected virtual Task InitializeStateAsync() => Task.CompletedTask;
protected override async Task OnInitializedAsync()
{
if (!OperatingSystem.IsBrowser())
{
await InitializeStateAsync();
return;
}
// When using InvokeAsync there can still be a flash. Use Invoke if possible.
var stateJson = Js is IJSInProcessRuntime inProcJs
? inProcJs.Invoke<string?>("getInnerText", [StateKey])
: await Js.InvokeAsync<string?>("getInnerText", [StateKey]);
if (string.IsNullOrWhiteSpace(stateJson))
{
await InitializeStateAsync();
return;
}
try
{
var buffer = Convert.FromBase64String(stateJson);
var json = Encoding.UTF8.GetString(buffer);
RestoreState(JsonSerializer.Deserialize<TState>(json)!);
}
catch
{
await InitializeStateAsync();
}
}
protected abstract void RestoreState(TState state);
private string Serialize()
{
if (State is null || OperatingSystem.IsBrowser())
return "";
var json = JsonSerializer.SerializeToUtf8Bytes(State);
var base64 = Convert.ToBase64String(json);
return $"<script id=\"{StateKey}\" type=\"text/template\">{base64}</script>";
}
} MyComponent.razor@inherits PersistentComponentBase<State>
...
@{ base.BuildRenderTree(__builder); } MyComponent.razor.csprotected override string StateKey => "my.state";
protected override async Task InitializeStateAsync()
{
// Load from database or whatever.
}
protected override void RestoreState(State state)
{
// Rehydrate state.
} |
Don't use that. @Markz878's workaround works well with none of the downsides. |
Unless I'm missing something, that only works until you have a nested component loading data. Then you're back to square one. It's the approach I took at first, but it doesn't work for very complex applications. |
What is the recommended navigation mode for Blazor if this bug (or limitation) is not high priority? It seems like Should we not be using |
Just wanted to do a quick drive-by - for anyone struggling with this, we have built a nuget package that allows persisting component state between prerender and the subsequent interactive render. It uses the approaches suggested and discussed in this issue. It may not be an ideal solution, but may help if anyone needs this functionality before it is implemented in the framework. |
|
Have spent around a day battling with this before realising this was known to work like this. I assumed at first that my property types were failing silently to deserialIze with a JsonException being swallowed or something... Having this as a part of .net is something I would love to see. |
This issue tracks adding a new mechanism to deliver state updates from enhanced navigations coming from the server to running runtimes.
The text was updated successfully, but these errors were encountered: