Skip to content

[Blazor] Support state persistence on root components #62331

Closed
@javiercn

Description

@javiercn

This currently fails because the parent component during prerendering is different than the component in interactive mode, and we lose the information about the key.

We need a new API on ComponentState to compute the @key for a given component (instead of us doing it internally on the value provider). The server and webassembly implementations need to provide the @key based on the information that we place on the marker on the page.

The issue is in GetSerializableKey

private static object? GetSerializableKey(ComponentState componentState)
{
if (componentState.ParentComponentState is not { } parentComponentState)
{
return null;
}
// Check if the parentComponentState has a `@key` directive applied to the current component.
var frames = parentComponentState.CurrentRenderTree.GetFrames();
for (var i = 0; i < frames.Count; i++)
{
ref var currentFrame = ref frames.Array[i];
if (currentFrame.FrameType != RenderTree.RenderTreeFrameType.Component ||
!ReferenceEquals(componentState.Component, currentFrame.Component))
{
// Skip any frame that is not the current component.
continue;
}
var componentKey = currentFrame.ComponentKey;
return !IsSerializableKey(componentKey) ? null : componentKey;
}
return null;
}
as it produces two different values when rendering statically vs when rendering interactively.

The fix here is to add a protected virtual object? GetComponentKey() method on ComponentState. The default implementation is equivalent to the one in GetSerializableKey but without the check for IsSerializableKey.

EndpointComponentState needs to override GetComponentKey check if the parent Component is an SSRRenderModeBoundary instance and in that case return the ComponentMarkerKey key (computing the key if necessary) (an internal helper method can be added to SSRRenderModeBoundary for that purpose)

WebAssemblyRenderer and RemoteRenderer need to override CreateComponentState and return a new subclasses WebAssemblyComponentState : ComponentState and RemoteComponentState: ComponentState that accept an optional ComponentMarkerKey as a parameter. These classes need to override GetComponentKey and return the provided ComponentMarkerKey when provided or rely on the default implementation otherwise.

The goal is that the key we generate is the same key across render modes. We might need to move internal static string ComputeKey(ComponentState componentState, string propertyName) into ComponentState itself as we will also have to filter out the SSRRenderModeBoundary from the list of parent component types.

We don't want a reference to SSRRenderModeBoundary inside Microsoft.AspNetCore.Components, but we need to filter out SSRRenderModeBoundary from the parent types.

We could do this by calling GetComponentRenderMode on the GrandParent (.Parent.Parent) of the ComponentState. If that doesn't return a rendermode, it means that .Parent is an SSRRenderModeBoundary component.

Metadata

Metadata

Labels

area-blazorIncludes: Blazor, Razor Components

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions