State Hierarchy

When using Blazor.State, it is important to understand how the state hierarchy works. This is important because Blazor.State works by persisting the state of the page after it has been prerendered and restoring it when the page is rendered for the second time.

Page State

A page's state is composed of the states of all its stateful components. This means that if a page contains multiple stateful components, the page's state will be composed of the states of all those components.

The page's state is persisted by encoding it into a JSON string. This string is then stored in the page's HTML, not visible to the user. When the page is rendered for the second time, the JSON string is decoded and the page's state is restored.

Component State Hierarchy

Consider the following component:

// MyComponent.razor
@inherits PersistentComponentBase

@Text

@ChildContent

@code
{
    [ComponentState]
    public string Text { get; set; } = "Some text";

    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

Let's break down the state hierarchy of this component:

  • This component has a single state property, Text - this property is the component's own state.

  • The component also allows populating it with nested content. This nested content can also have it's own state, in that case this will be this component's nested state.

Let's consider the following usage of this component:

<MyComponent>
    <MyComponent />
</MyComponent>

In this case, this page's state hierarchy will look something like the following:

{
    "Text": "Some text",
    "n__MyComponent": {
        "Text": "Some text"
    }
}

As you can see, the page's state is composed of the states of all its stateful components. In this case, the page contains two instances of MyComponent, one nested inside the other.

  • The state of the nested component is stored in the parent component's state.

  • When nesting components, the state of the nested component is prefixed with n__ to avoid conflicts with the parent component's state.

  • For the name of the nested component's state property inside of it's parent component's state, the name of the nested component's type is used: MyComponent.

Component StateId

When persisting the state of a component inside a state hierarchy, Blazor.State uses the component's type name as an identifier by default. This approach works well in cases where a single state parent does not contain multiple stateful components of the same type.

Consider the following example:

<MyComponent>
    <MyComponent />
    <MyComponent />
</MyComponent>

In this case, the page's state hierarchy should look something like the following:

{
    "Text": "Some text",
    "n__MyComponent": {
        "Text": "Some text"
    },
    "n__MyComponent": {
        "Text": "Some text"
    }
}

But this is not possible, because the state of the parent component is a JSON object, and JSON objects cannot contain multiple properties with the same name. Even if it was possible, it would not be possible to distinguish between the two nested components when restoring such state.

To solve this problem, Blazor.State introduces a component parameter StateId. Component StateIds are unique string identifiers that are used to distinguish between multiple components located under the same state parent.

Consider the following example:

<MyComponent>
    <MyComponent StateId="NestedComponent1" />
    <MyComponent StateId="NestedComponent2" />
</MyComponent>

In this case, the page's state hierarchy should now look something like the following:

{
    "Text": "Some text",
    "n__NestedComponent1": {
        "Text": "Some text"
    },
    "n__NestedComponent2": {
        "Text": "Some text"
    }
}

As you can see, the state of the nested components is stored in the parent component's state, and the state of each nested component is prefixed with n__ and suffixed with the component's StateId. This way, Blazor.State can distinguish between multiple components of the same type located under the same parent stateful component - and restore their states correctly, with each component receiving it's own state.