There are two pages in my app:Page1.razor (home) and Page2.razor. There is no problem rendering first page. But when I navigate to second page, there is flicker problem. I put 1000 ms sleep to better see the flickler. Whichever the page, this problem exists.
- Open the app
Page1renders with no problem- Navigate to
Page2, flicker problem - Open an incognito browser page
- Paste
Page2link - There is no problem rendering
Page2 - Navigate to
Page1, flicker problem
Although using a global InteractiveAutoRender mode (in App.razor) fixes the problem, my app uses no global render mode. I don't want this. I probably miss something in the component lifecycle. Can't figure out what. Anyone can help? Thank you for your time.
Bug produced github repo:https://github.com/kemgn/PersistenceBug
App.razor:
<head> . .<ImportMap /><HeadOutlet /></head><body><Routes /><script src="_framework/blazor.web.js"></script></body> Page1.razor:
@page "/"@inject HttpClient Http@using System.Text.Json.Serialization@using System.Collections.ObjectModel@using static PersistanceBug.Client.Pages.Page1@rendermode @(new InteractiveAutoRenderMode(true))@inherits PersistentDataComponentBase<Post[]><h3>Page 1 (Home)</h3><p>Calling mock API from: https://jsonplaceholder.typicode.com/posts</p><NavLink href="page2">Go to Page 2</NavLink><ul> @foreach (var post in posts) {<li>@post.Title</li> }</ul>@code { private Post[] posts = Array.Empty<Post>(); protected override string DataKey => "page1persist"; protected override async Task<Post[]?> LoadDataAsync() { Post[]? result = await Http.GetFromJsonAsync<Post[]>("https://jsonplaceholder.typicode.com/posts").ConfigureAwait(true); return result ?? []; } protected override void OnDataLoaded(Post[]? data) { if (data is null) return; posts = data; } protected override Post[]? PrepareDataForPersistence(Post[]? data) { return posts?.ToArray(); }}Page2.razor:
@page "/page2"@inject HttpClient Http@using System.Text.Json.Serialization@using System.Collections.ObjectModel@using static PersistanceBug.Client.Pages.Page2@rendermode @(new InteractiveAutoRenderMode(true))@inherits PersistentDataComponentBase<Comment[]><h3>Page 2</h3><p>Calling mock API from: https://jsonplaceholder.typicode.com/comments</p><NavLink href="/">Go to Page 1</NavLink><ul> @foreach (var comment in comments) {<li>@comment.Name</li> }</ul>@code { private Comment[] comments = Array.Empty<Comment>(); protected override string DataKey => "page2persist"; protected override async Task<Comment[]?> LoadDataAsync() { Comment[]? result = await Http.GetFromJsonAsync<Comment[]>("https://jsonplaceholder.typicode.com/Comments").ConfigureAwait(true); return result ?? []; } protected override void OnDataLoaded(Comment[]? data) { if (data is null) return; comments = data; } protected override Comment[]? PrepareDataForPersistence(Comment[]? data) { return comments?.ToArray(); }}Persistence.cs
using System;using System.Threading.Tasks;using Microsoft.AspNetCore.Components;namespace PersistanceBug.Client.Pages{ public abstract class PersistentDataComponentBase<T> : Microsoft.AspNetCore.Components.ComponentBase, IDisposable { [Inject] protected PersistentComponentState ApplicationState { get; set; } = default!; private PersistingComponentStateSubscription persistingSubscription; protected T? Data { get; set; } private bool disposed; protected abstract string DataKey { get; } protected abstract Task<T?> LoadDataAsync(); protected abstract void OnDataLoaded(T? data); protected abstract T? PrepareDataForPersistence(T? data); protected override async Task OnInitializedAsync() { await base.OnInitializedAsync().ConfigureAwait(true); Thread.Sleep(1000); persistingSubscription = ApplicationState.RegisterOnPersisting(persistDataAsync); bool restored = ApplicationState.TryTakeFromJson(DataKey, out T? restoredData); if (!restored) { T? apiData = await LoadDataAsync().ConfigureAwait(false); OnDataLoaded(apiData); if (!Equals(Data, default(T))) { Console.WriteLine($"✅ {DataKey} verisi yüklendi"); } } else { OnDataLoaded(restoredData); } } private Task persistDataAsync() { T? dataToStore = PrepareDataForPersistence(Data); ApplicationState.PersistAsJson(DataKey, dataToStore); return Task.CompletedTask; } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { persistingSubscription.Dispose(); } disposed = true; } } ~PersistentDataComponentBase() { Dispose(disposing: false); } }}