@channel-state/core
The core, framework-agnostic library for channel-state.
Overview
@channel-state/core is the foundational package of the channel-state ecosystem. It provides the ChannelStore class, a powerful, zero-dependency solution for state management that works in any JavaScript environment. It enables seamless, real-time state synchronization across browser tabs and windows using the native BroadcastChannel and IndexedDB APIs.
Installation
npm
npm install @channel-state/coreyarn
yarn add @channel-state/corepnpm
pnpm add @channel-state/corebun
bun add @channel-state/coreCDN Usage
For direct usage in the browser, you can use the UMD build from a CDN like jsDelivr or unpkg:
<script src="https://cdn.jsdelivr.net/npm/@channel-state/core@0"></script>Playground
Explore and experiment with channel-state in a live environment using our interactive playground.
This playground provides a simple example of how to use @channel-state/core and @channel-state/react together.
Note: To see the cross-tab state synchronization in action, open the preview in a new tab.
API Reference
ChannelStore<T>
The primary class for creating and managing a synchronized state.
Constructor
new ChannelStore<T>(options: ChannelStoreOptions<T>)
| Parameter | Type | Description |
|---|---|---|
options |
ChannelStoreOptions |
An object containing the configuration for the store. |
ChannelStoreOptions
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string |
Yes | - | A unique name for the channel. This is used for the BroadcastChannel and as the IndexedDB database name. |
initial |
T |
Yes | - | The initial value of the state. |
persist |
boolean |
No | false |
If true, the state will be persisted to IndexedDB and restored on initialization. |
Properties
| Property | Type | Description |
|---|---|---|
status |
StoreStatus |
The current status of the store: 'initializing', 'ready', or 'destroyed'. |
Methods
| Method | Signature | Description |
|---|---|---|
get() |
(): T |
Synchronously returns the current state. |
set() |
(value: T): void |
Sets a new state, broadcasts it to other contexts, and persists it if persist is enabled. The method itself is synchronous, but persistence happens in the background. |
subscribe() |
(callback: (value: T) => void): () => void |
Subscribes to state changes. The callback receives the new state. Returns an unsubscribe function. |
subscribeStatus() |
(callback: (status: StoreStatus) => void): () => void |
Subscribes to store status changes. The callback receives the new status. Returns an unsubscribe function. |
destroy() |
(): void |
Closes the BroadcastChannel and IndexedDB connections and cleans up all subscribers. The store instance cannot be used afterward. |
reset() |
(): Promise<void> |
Resets the state to its initial value and broadcasts the change. |
Example Usage
import { ChannelStore } from '@channel-state/core'
// 1. Create a new store instance. This should be done once and shared.
const counterStore = new ChannelStore<number>({
// A unique name for the channel, used for BroadcastChannel and IndexedDB.
name: 'shared-counter',
// The initial state of the store if no persisted state is found.
initial: 0,
// Set to true to persist the state to IndexedDB.
persist: true,
})
// 2. Subscribe to status changes to know when the store is ready.
// This is crucial when `persist` is true, as it takes time to load from IndexedDB.
const unsubscribeStatus = counterStore.subscribeStatus((status) => {
console.log('Store status is now:', status)
// 3. Once the store is ready, you can safely interact with it.
if (status === 'ready') {
// Get the current state. This will be the persisted value if it exists.
const currentCount = counterStore.get()
console.log('Initial or persisted count:', currentCount)
// Set a new state. This will be broadcast to other tabs.
counterStore.set(currentCount + 1)
}
})
// 4. Subscribe to state changes from any tab.
const unsubscribeState = counterStore.subscribe((newCount) => {
console.log('Count has changed to:', newCount)
// Here you would typically update your UI.
})
// 5. It's important to clean up when your application or component unmounts.
// The function below should be called from your framework's lifecycle hook
// (e.g., `useEffect` in React, `onUnmounted` in Vue, `onDestroy` in Svelte).
function cleanup() {
unsubscribeStatus()
unsubscribeState()
counterStore.destroy()
}
// The line below is a generic example and is commented out because the specific
// implementation depends on your application's architecture.
// window.addEventListener('beforeunload', cleanup);