diff --git a/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs b/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs index 30e537a418a3..35ad18735816 100644 --- a/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs +++ b/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs @@ -143,9 +143,9 @@ internal void RestoreProperty() Log.RestoringValueFromState(_logger, _storageKey, _propertyType.Name, _propertyName); var sequence = new ReadOnlySequence(data!); _lastValue = _customSerializer.Restore(_propertyType, sequence); + _ignoreComponentPropertyValue = true; if (!skipNotifications) { - _ignoreComponentPropertyValue = true; _subscriber.NotifyCascadingValueChanged(ParameterViewLifetime.Unbound); } } @@ -160,9 +160,9 @@ internal void RestoreProperty() { Log.RestoredValueFromPersistentState(_logger, _storageKey, _propertyType.Name, "null", _propertyName); _lastValue = value; + _ignoreComponentPropertyValue = true; if (!skipNotifications) { - _ignoreComponentPropertyValue = true; _subscriber.NotifyCascadingValueChanged(ParameterViewLifetime.Unbound); } } diff --git a/src/Components/Components/test/PersistentValueProviderComponentSubscriptionTests.cs b/src/Components/Components/test/PersistentValueProviderComponentSubscriptionTests.cs index 273246516f7a..fe0be71cfa25 100644 --- a/src/Components/Components/test/PersistentValueProviderComponentSubscriptionTests.cs +++ b/src/Components/Components/test/PersistentValueProviderComponentSubscriptionTests.cs @@ -256,14 +256,14 @@ public async Task GetOrComputeLastValue_FollowsCorrectValueTransitionSequence() // Pre-populate the state with serialized data var key = PersistentStateValueProviderKeyResolver.ComputeKey(componentState, nameof(TestComponent.State)); - appState[key] = JsonSerializer.SerializeToUtf8Bytes("first-restored-value", JsonSerializerOptions.Web); + appState[key] = JsonSerializer.SerializeToUtf8Bytes("first-persisted-value", JsonSerializerOptions.Web); await manager.RestoreStateAsync(new TestStore(appState), RestoreContext.InitialValue); await renderer.Dispatcher.InvokeAsync(() => renderer.RenderRootComponentAsync(componentId, ParameterView.Empty)); var cascadingParameterInfo = CreateCascadingParameterInfo(nameof(TestComponent.State), typeof(string)); // Act & Assert - First call: Returns restored value from state - Assert.Equal("first-restored-value", component.State); + Assert.Equal("first-persisted-value", provider.GetCurrentValue(componentState, cascadingParameterInfo)); // Change the component's property value component.State = "updated-property-value"; @@ -279,7 +279,7 @@ public async Task GetOrComputeLastValue_FollowsCorrectValueTransitionSequence() }; // Simulate invoking the callback with a value update. await renderer.Dispatcher.InvokeAsync(() => manager.RestoreStateAsync(new TestStore(newState), RestoreContext.ValueUpdate)); - Assert.Equal("second-restored-value", component.State); + Assert.Equal("second-restored-value", provider.GetCurrentValue(componentState, cascadingParameterInfo)); component.State = "another-updated-value"; // Other calls: Returns the updated value from state diff --git a/src/Components/test/E2ETest/Tests/StatePersistenceTest.cs b/src/Components/test/E2ETest/Tests/StatePersistenceTest.cs index a05fbfa05979..007c7ce5de16 100644 --- a/src/Components/test/E2ETest/Tests/StatePersistenceTest.cs +++ b/src/Components/test/E2ETest/Tests/StatePersistenceTest.cs @@ -116,15 +116,22 @@ public void CanRenderComponentWithPersistedState(bool suppressEnhancedNavigation // In each case, we validate that the state is available until the initial set of components first render reaches quiescence. Similar to how it works for Server and WebAssembly. // For server we validate that the state is provided every time a circuit is initialized. [Theory] - [InlineData(typeof(InteractiveServerRenderMode), (string)null)] - [InlineData(typeof(InteractiveServerRenderMode), "ServerStreaming")] - [InlineData(typeof(InteractiveWebAssemblyRenderMode), (string)null)] - [InlineData(typeof(InteractiveWebAssemblyRenderMode), "WebAssemblyStreaming")] - [InlineData(typeof(InteractiveAutoRenderMode), (string)null)] - [InlineData(typeof(InteractiveAutoRenderMode), "AutoStreaming")] + [InlineData(typeof(InteractiveServerRenderMode), (string)null, "yes")] + [InlineData(typeof(InteractiveServerRenderMode), "ServerStreaming", "yes")] + [InlineData(typeof(InteractiveWebAssemblyRenderMode), (string)null, "yes")] + [InlineData(typeof(InteractiveWebAssemblyRenderMode), "WebAssemblyStreaming", "yes")] + [InlineData(typeof(InteractiveAutoRenderMode), (string)null, "yes")] + [InlineData(typeof(InteractiveAutoRenderMode), "AutoStreaming", "yes")] + [InlineData(typeof(InteractiveServerRenderMode), (string)null, null)] + [InlineData(typeof(InteractiveServerRenderMode), "ServerStreaming", null)] + [InlineData(typeof(InteractiveWebAssemblyRenderMode), (string)null, null)] + [InlineData(typeof(InteractiveWebAssemblyRenderMode), "WebAssemblyStreaming", null)] + [InlineData(typeof(InteractiveAutoRenderMode), (string)null, null)] + [InlineData(typeof(InteractiveAutoRenderMode), "AutoStreaming", null)] public void CanUpdateComponentsWithPersistedStateAndEnhancedNavUpdates( Type renderMode, - string streaming) + string streaming, + string key) { var mode = renderMode switch { @@ -136,7 +143,7 @@ public void CanUpdateComponentsWithPersistedStateAndEnhancedNavUpdates( // Navigate to a page without components first to make sure that we exercise rendering components // with enhanced navigation on. - NavigateToInitialPage(streaming, mode); + NavigateToInitialPage(streaming, mode, key); if (mode == "auto") { BlockWebAssemblyResourceLoad(); @@ -156,22 +163,36 @@ public void CanUpdateComponentsWithPersistedStateAndEnhancedNavUpdates( UnblockWebAssemblyResourceLoad(); Browser.Navigate().Refresh(); - NavigateToInitialPage(streaming, mode); + NavigateToInitialPage(streaming, mode, key); Browser.Click(By.Id("call-blazor-start")); Browser.Click(By.Id("page-with-components-link-and-declarative-state")); RenderComponentsWithDeclarativePersistentStateAndValidate(mode, renderMode, streaming, interactiveRuntime: "wasm", stateValue: "other"); } - void NavigateToInitialPage(string streaming, string mode) + void NavigateToInitialPage(string streaming, string mode, string key) { - if (streaming == null) + if (key == null) { - Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&suppress-autostart"); + if (streaming == null) + { + Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&suppress-autostart"); + } + else + { + Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&streaming-id={streaming}&suppress-autostart"); + } } else { - Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&streaming-id={streaming}&suppress-autostart"); + if (streaming == null) + { + Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&key={key}&suppress-autostart"); + } + else + { + Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&key={key}&streaming-id={streaming}&suppress-autostart"); + } } } } diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithDeclarativeEnhancedNavigationPersistentComponents.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithDeclarativeEnhancedNavigationPersistentComponents.razor index c314dac85843..43d04d8e6938 100644 --- a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithDeclarativeEnhancedNavigationPersistentComponents.razor +++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithDeclarativeEnhancedNavigationPersistentComponents.razor @@ -14,15 +14,30 @@

Streaming id:@StreamingId

@if (_renderMode != null) { - @if (!string.IsNullOrEmpty(StreamingId)) + @if(!string.IsNullOrEmpty(KeyValue)) { - + @if (!string.IsNullOrEmpty(StreamingId)) + { + + } + else + { + + } } else { - + @if (!string.IsNullOrEmpty(StreamingId)) + { + + } + else + { + + } } } + @if (!string.IsNullOrEmpty(StreamingId)) { End streaming @@ -41,6 +56,8 @@ [SupplyParameterFromQuery(Name = "server-state")] public string ServerState { get; set; } + [SupplyParameterFromQuery(Name = "key")] public string KeyValue { get; set; } + protected override void OnInitialized() { if (!string.IsNullOrEmpty(RenderMode)) diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithoutComponents.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithoutComponents.razor index a72bdbe97778..fd2807311980 100644 --- a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithoutComponents.razor +++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithoutComponents.razor @@ -2,14 +2,19 @@

This page does not render any component. We use it to test that persisted state is only provided at the time interactive components get activated on the page.

-Go to page with components - -Go to page with components and state - -Go to page with declarative state components + @code { [SupplyParameterFromQuery(Name = "render-mode")] public string RenderMode { get; set; } [SupplyParameterFromQuery(Name = "streaming-id")] public string StreamingId { get; set; } + + [SupplyParameterFromQuery(Name = "key")] public string KeyValue { get; set; } }