StatePort Pattern
The StatePort<T> interface is the foundation of framework-agnostic state management in xndr.
Interface Definition
interface StatePort<T> {
/**
* Get current value
*/
get(): T;
/**
* Set new value (supports both direct value and updater function)
*/
set(value: T | ((prev: T) => T)): void;
/**
* Subscribe to changes. Returns unsubscribe function.
* Optional: some systems (like React) might not support subscriptions
*/
subscribe?(callback: (value: T) => void): () => void;
}
Methods
get(): T
Returns the current value of the StatePort.
Returns: The current value of type T
Example:
const count = new ReactiveValue(0);
const currentValue = count.get(); // 0
set(value: T | ((prev: T) => T)): void
Updates the value. Accepts either:
- A direct value of type
T - An updater function
(prev: T) => Tthat receives the previous value and returns the new value
Parameters:
value: T | ((prev: T) => T)- The new value or updater function
Example:
const count = new ReactiveValue(0);
// Direct value
count.set(10);
// Updater function (useful for immutable updates)
count.set((prev) => prev + 1);
count.set((prev) => prev * 2);
subscribe?(callback: (value: T) => void): () => void
Subscribes to value changes. This method is optional—some systems (like React's useState) don't support subscriptions.
Parameters:
callback: (value: T) => void- Function called when the value changes
Returns: An unsubscribe function that removes the subscription
Example:
const count = new ReactiveValue(0);
const unsubscribe = count.subscribe?.((value) => {
console.log('Count changed to:', value);
});
count.set(10); // Logs: "Count changed to: 10"
count.set(20); // Logs: "Count changed to: 20"
// Unsubscribe when done
unsubscribe?.();
Why StatePort?
The StatePort pattern provides:
- Framework Independence - Your business logic doesn't depend on React, Solid, Svelte, or any specific framework
- Testability - Test your logic without framework mocking
- Reusability - Share code between different frameworks and projects
- Flexibility - Swap frameworks without rewriting business logic
Implementing StatePort
All reactive primitives in @xndrjs/core implement StatePort:
ReactiveValue<T>implementsStatePort<T>ReactiveObject<T>implementsStatePort<T>ReactiveArray<T>implementsStatePort<T[]>ReactiveSet<T>implementsStatePort<Set<T>>ReactiveMap<K, V>implementsStatePort<Map<K, V>>ComputedValue<T>implementsStatePort<T>(read-only)
Framework Integration
Framework adapters convert StatePort instances to framework-specific reactive primitives:
- React:
useReactiveValue(port)returns the current value and re-renders on changes - Solid:
useReactiveValue(port)returns a Solid accessor function - Svelte:
reactiveValue(() => port)returns a Svelte store
See the Framework Adapters documentation for details.
Type Utilities
The package exports type utilities for working with StatePort:
import type { StatePortValue, IsStatePort } from '@xndrjs/core';
// Extract value type from a StatePort
type Count = StatePortValue<typeof countPort>; // number
// Check if a type is a StatePort
type IsPort = IsStatePort<ReactiveValue<number>>; // true
Next Steps
- Learn about ReactiveValue - the simplest StatePort implementation
- Explore ComputedValue - derived reactive values
- Check out Framework Adapters - connecting StatePort to frameworks