Built-In Navigation and Lifecycle
Overview
Reservoir's built-in client features represent navigation and application milestones as actions and state. This gives pages and diagnostics the same explicit state model used by application features.
Registration
All entry points extend IReservoirBuilder and are supplied by Mississippi.Reservoir.Client:
| Method | Registers |
|---|---|
AddReservoirBlazorBuiltIns() | Navigation and lifecycle features |
AddBuiltInNavigation() | Navigation state, location reducer, and navigation effect |
AddBuiltInLifecycle() | Lifecycle state and milestone reducers |
Registration extensions live in Mississippi.Reservoir.Client.BuiltIn, .BuiltIn.Navigation, and .BuiltIn.Lifecycle. Navigation action types live in .BuiltIn.Navigation.Actions; lifecycle action types live in .BuiltIn.Lifecycle.Actions, while LifecycleState and LifecyclePhase live in .BuiltIn.Lifecycle.State. C# does not import child namespaces automatically. Spring startup registers both features inside its Reservoir callback.
Browser Location Observation
Render one ReservoirNavigationProvider at the application root to connect NavigationManager.LocationChanged with the store. This is the root-markup pattern used by Spring:
@using Mississippi.Reservoir.Client.BuiltIn.Components
<ReservoirNavigationProvider/>
The provider dispatches the current URI on initialization, forwards subsequent location changes, and unsubscribes when disposed. Its initial notification contributes to NavigationCount; that count represents handled location notifications rather than only user clicks.
Before the provider's initial dispatch, or if no provider is rendered, registered NavigationState has CurrentUri = null, PreviousUri = null, IsNavigationIntercepted = false, and NavigationCount = 0. The initial dispatch records the current browser URI and increments the count to one.
Navigation Actions
The action namespace is Mississippi.Reservoir.Client.BuiltIn.Navigation.Actions.
| Action | Parameters and defaults | Purpose |
|---|---|---|
NavigateAction | Uri, ForceLoad = false | Navigate through NavigationManager |
ReplaceRouteAction | Uri, ForceLoad = false | Navigate while replacing the history entry |
SetQueryParamsAction | Parameters, ReplaceHistory = true | Update query parameters on the current URI |
ScrollToAnchorAction | AnchorId without #, ReplaceHistory = false | Navigate to the current page's fragment |
LocationChangedAction | Location, IsNavigationIntercepted | Record an observed browser location |
Use these actions for application navigation. The effect accepts relative application paths and absolute URIs on the same origin; use normal links for external destinations. For a cross-origin absolute URI in NavigateAction or ReplaceRouteAction, the effect throws, but the store catches the effect failure. Dispatch returns without navigation or an error action, so do not wait for a location update or expect to catch that failure from Dispatch. Supported navigation follows Blazor's NavigationManager behavior, including history and force-load semantics.
NavigationState uses feature key reservoir:navigation and exposes CurrentUri, PreviousUri, IsNavigationIntercepted, and NavigationCount. The location reducer moves the current URI to previous, records the new URI/interception flag, and increments the count.
For SetQueryParamsAction, use Blazor's supported query values: bool, DateOnly, DateTime, decimal, double, float, Guid, int, long, and string, including their supported nullable and array forms. The Blazor navigation reference defines this contract. Validate values before dispatch: unsupported object types are rejected by Blazor, and the live effect boundary catches that exception without producing a navigation error action.
To remove an existing query parameter, include its key in SetQueryParamsAction.Parameters with a null value. Omitting the key preserves its current query value. This follows the same Blazor query-update contract used for additions and replacements.
Lifecycle Milestones
LifecycleState uses feature key reservoir:lifecycle. It starts in NotStarted with null timestamps.
| Application action | State update |
|---|---|
AppInitAction(InitializedAt) | Sets Phase = Initializing and InitializedAt |
AppReadyAction(ReadyAt) | Sets Phase = Ready and ReadyAt |
The application dispatches these actions at the milestones it defines. Supply timestamps in the payload, using the application's time source. The reducers use those values directly, keeping the transition deterministic and easy to test. Each reducer updates the fields shown above and preserves the other fields.
Order and deduplicate lifecycle milestones in the application. The reducers apply each supplied phase and timestamp directly: AppInitAction after AppReadyAction returns the phase to Initializing while preserving ReadyAt, and repeated actions replace their corresponding timestamps.
Use lifecycle state to explain what initialization has completed, and give an AI assistant precise milestone actions and expected state when adding startup behavior.
Source and Verification
- Built-in registration.
- Navigation provider, actions, and effect.
- Lifecycle reducers.
- Built-in client tests.
Summary
Observe navigation through the root provider and supply lifecycle milestones from application code. Both become explicit actions and state that pages, tests, and development tools can inspect.
Next Steps
- Enable DevTools to inspect these actions.
- Middleware reference for dispatch interception.