@gooonzick/wizard-state
v1.0.0
Published
A state management layer for multi-step wizards. Provides fine-grained subscription channels for efficient React component rendering when using `useSyncExternalStore`.
Readme
@gooonzick/wizard-state
A state management layer for multi-step wizards. Provides fine-grained subscription channels for efficient React component rendering when using useSyncExternalStore.
Overview
WizardStateManager wraps a WizardMachine instance and adds a subscription-based state notification system. This enables React hooks to subscribe to specific state channels (state, navigation, validation, loading) for selective re-renders, avoiding unnecessary updates.
Installation
pnpm add @gooonzick/wizard-state @gooonzick/wizard-coreBasic Usage
import { WizardMachine } from "@gooonzick/wizard-core";
import { WizardStateManager } from "@gooonzick/wizard-state";
// Create a wizard machine
const machine = new WizardMachine(wizardDefinition, context, initialData);
// Wrap it with the state manager
const manager = new WizardStateManager(machine, "step-1");
// Subscribe to state changes
const unsubscribe = manager.subscribe(() => {
console.log("State changed");
});
// Subscribe to specific channel
const navUnsubscribe = manager.subscribe(
() => {
console.log("Navigation state changed");
},
"navigation"
);
// Handle machine state changes
manager.handleStateChange(newState, oldState);
// Clean up
unsubscribe();
navUnsubscribe();API
Constructor
new WizardStateManager<T extends WizardData>(
machine: WizardMachine<T>,
initialStepId: StepId
)Creates a state manager wrapping the provided machine.
Subscription Methods
subscribe(listener, channel?)
Subscribes to state changes on a specific channel or all channels.
// Subscribe to all channels (default)
const unsub = manager.subscribe(() => console.log("Any change"));
// Subscribe to specific channel
const navUnsub = manager.subscribe(
() => console.log("Navigation changed"),
"navigation"
);Channels:
"state"- Data or current step changes"navigation"- Navigation capabilities (canGoNext, canGoPrevious)"validation"- Validation state or errors"loading"- Loading flags (isValidating, isSubmitting, isNavigating)"all"- All channels (default)
Snapshot Methods
getStateSnapshot()
Returns cached state snapshot for the current step and data.
const snapshot = manager.getStateSnapshot();
console.log(snapshot.currentStepId, snapshot.data);getNavigationSnapshot()
Returns cached navigation state (async-computed).
const navSnap = manager.getNavigationSnapshot();
console.log(navSnap.canGoNext, navSnap.canGoPrevious);getValidationSnapshot()
Returns cached validation state.
const validSnap = manager.getValidationSnapshot();
console.log(validSnap.isValid, validSnap.validationErrors);getLoadingSnapshot()
Returns cached loading state.
const loadingSnap = manager.getLoadingSnapshot();
console.log(loadingSnap.isValidating, loadingSnap.isSubmitting);State Management
handleStateChange(newState, oldState)
Called when the machine state changes. Automatically determines which channels to notify based on what actually changed.
machine.subscribe((newState, oldState) => {
manager.handleStateChange(newState, oldState);
});setLoadingState(update)
Updates loading state and notifies loading channel subscribers.
manager.setLoadingState({ isValidating: true });Notification
notifySubscribers(channels)
Manually notify subscribers of specific channels.
manager.notifySubscribers(["state", "navigation"]);Machine Access
getMachine()
Get direct access to the underlying machine.
const machine = manager.getMachine();getCurrentStep()
Get the current step definition.
const step = manager.getCurrentStep();getVisitedSteps()
Get list of visited step IDs.
const visited = manager.getVisitedSteps();getStepHistory()
Get ordered history of step navigation.
const history = manager.getStepHistory();Design Philosophy
- Cached Snapshots - All snapshot getters return the same reference until the underlying data actually changes, enabling
useSyncExternalStorestability - Channel-Based Subscriptions - Listeners only receive notifications for the specific channels they care about, reducing unnecessary React renders
- Async Navigation - Navigation capabilities are computed asynchronously in the background to avoid blocking renders
See Also
- @gooonzick/wizard-core - Core state machine
- @gooonzick/wizard-react - React hooks using WizardStateManager
