Skip to main content

Feature State

Overview

Feature states are independent slices of the Reservoir state tree. Each feature state represents a self-contained piece of application state (UI, forms, session) with its own reducers and effects. (IFeatureState)

What Is Feature State?

A feature state is an immutable record that implements IFeatureState. Each feature state:

  • Has a unique FeatureKey that identifies it in the store
  • Is registered independently with its own reducers and effects
  • Can be accessed via store.GetState<TState>()
public interface IFeatureState
{
static abstract string FeatureKey { get; }
}

The FeatureKey must be unique across all registered feature states. (IFeatureState)

When to Use Feature State

Use feature state to model localized UI, forms, and session-oriented state that should live in the store.

Defining Feature State

Feature states are immutable records with a static FeatureKey:

// From Spring sample
internal sealed record EntitySelectionState : IFeatureState
{
public static string FeatureKey => "entitySelection";

public string? EntityId { get; init; }
}

(EntitySelectionState)

// From Reservoir.L0Tests
private sealed record TestFeatureState : IFeatureState
{
public static string FeatureKey => "test-feature";

public int Counter { get; init; }
}

(StoreTests.TestFeatureState)

Immutable Record Usage

In the Spring sample, reducers update feature state by returning new record instances with state with { ... }. (Spring sample reducer)

Registering Feature State

Feature state is registered automatically when you add reducers or effects:

// AddReducer automatically registers the feature state
services.AddReducer<SetEntityIdAction, EntitySelectionState>(
(state, action) => state with { EntityId = action.EntityId });

// AddActionEffect also registers the feature state
services.AddActionEffect<EntitySelectionState, MyEffect>();

For feature states without reducers or effects, register directly:

public static IServiceCollection AddFeatureState<TState>(
this IServiceCollection services
)
where TState : class, IFeatureState, new();

(ReservoirRegistrations.AddFeatureState)

Automatic Deduplication

AddFeatureState uses TryAddEnumerable to prevent duplicate registrations. You can call it multiple times for the same state type without side effects. (ReservoirRegistrations source)

Accessing Feature State

Use store.GetState<TState>() to retrieve the current state:

EntitySelectionState selection = store.GetState<EntitySelectionState>();
string? currentEntityId = selection.EntityId;

From a Blazor component inheriting StoreComponent:

private EntitySelectionState Selection => GetState<EntitySelectionState>();

(IStore.GetState, Store.GetState)

If the feature state is not registered, GetState throws:

InvalidOperationException: No feature state registered for 'entitySelection'.
Call AddFeatureState<EntitySelectionState>() during service registration.

How Feature State Works

When the store is created, it collects all IFeatureStateRegistration instances from DI:

Each registration provides:

PropertyDescription
FeatureKeyUnique identifier for the feature state
InitialStateDefault state instance (created via new TState())
RootReducerComposite reducer for this feature (if any)
RootActionEffectComposite effect for this feature (if any)

(IFeatureStateRegistration, FeatureStateRegistration, Store constructor)

Feature Key and Initialization

The FeatureKey must be unique across all registered feature states. Initial state instances are created via new TState() in the registration path, so each feature state type must have a parameterless constructor. (IFeatureState, FeatureStateRegistration)

Summary

ConceptDescription
Feature stateImmutable record implementing IFeatureState
FeatureKeyUnique string identifier for the state slice
RegistrationAutomatic via AddReducer/AddActionEffect, or explicit via AddFeatureState
Accessstore.GetState<TState>() or GetState<TState>() in components
InitializationInitial state is created via new TState() in the registration path

(IFeatureState, IFeatureStateRegistration, ReservoirRegistrations)

Next Steps

  • Reservoir Overview - See how feature state participates in dispatch
  • Store - Understand the central hub that coordinates feature states, reducers, and effects
  • Reducers - Learn how reducers update feature state
  • Effects - Learn how effects perform async operations