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
FeatureKeythat 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; }
}
// From Reservoir.L0Tests
private sealed record TestFeatureState : IFeatureState
{
public static string FeatureKey => "test-feature";
public int Counter { get; init; }
}
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)
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:
| Property | Description |
|---|---|
FeatureKey | Unique identifier for the feature state |
InitialState | Default state instance (created via new TState()) |
RootReducer | Composite reducer for this feature (if any) |
RootActionEffect | Composite 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
| Concept | Description |
|---|---|
| Feature state | Immutable record implementing IFeatureState |
| FeatureKey | Unique string identifier for the state slice |
| Registration | Automatic via AddReducer/AddActionEffect, or explicit via AddFeatureState |
| Access | store.GetState<TState>() or GetState<TState>() in components |
| Initialization | Initial 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