API REFERENCE · UTILITIES
@dojo-ng/store
Connects Lit components to an external store, so a component re-renders when the state it uses changes.
npm install @dojo-ng/store
Create a store, then select the value a component uses.
import { LitElement, html } from "lit";
import { createStore, StoreController } from "@dojo-ng/store";
export const counter = createStore((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}));
class CountButton extends LitElement {
// Re-renders only when `count` changes.
#count = new StoreController(this, counter, (s) => s.count);
render() {
return html`<button @click=${() => counter.getState().increment()}>
Clicked ${this.#count.value} times
</button>`;
}
}
customElements.define("count-button", CountButton);
What is in the package
StoreController: a Lit reactive controller. Pass a store and a selector. The host re-renders only when the selected value changes (compared withObject.is).createStore: re-exported from Zustand's vanilla build, for an app that does not have a store yet.ReadableStore: the type the controller needs. Any object withgetState()andsubscribe(listener)works, so you can use Zustand, Valtio, Nano Stores (with a small adapter), or your own store.
Observable interop
- These helpers are for apps that already use RxJS or another library that follows the
Symbol.observableprotocol. Nothing in this package needs RxJS. toObservable(store): an observable that emits the current state at once, then every change.fromObservable(input, initial): aReadableStorethat follows an observable.initialis required, because an observable has no current value until it emits.ObservableController: re-renders the host on every value from an observable.
Examples
Bridge to RxJS
Turn an observable into a store, or a store into an observable.
import { from, interval } from "rxjs";
import { fromObservable, toObservable } from "@dojo-ng/store";
import { counter } from "./counter.js";
// An observable as a store. Use it with StoreController or the context registry.
const ticks = fromObservable(interval(1000), 0);
ticks.getState(); // 0 until the first tick
// A store as an observable. Use it with RxJS operators.
from(toObservable(counter)).subscribe((state) => console.log(state.count));
Functions
fromObservable
function fromObservable<T>(input: ObservableInput<T>, initial: T): ReadableStore<T>;
resolveObservable
function resolveObservable<T>(input: ObservableInput<T>): ObservableLike<T>;
toObservable
function toObservable<T>(store: ReadableStore<T>): ObservableLike<T>;
toUnsubscribeFn
function toUnsubscribeFn(result: Subscription | (() => void)): () => void;
Classes
ObservableController
class ObservableController<T> implements ReactiveController {
value: T;
constructor(host: ReactiveControllerHost, input: ObservableInput<T>, initial: T);
hostConnected(): void;
hostDisconnected(): void;
}
StoreController
class StoreController<T, S = T> implements ReactiveController {
value: S;
constructor(host: ReactiveControllerHost, store: ReadableStore<T>, selector?: (state: T) => S);
hostConnected(): void;
hostDisconnected(): void;
}
Values
observableSymbol
const observableSymbol: symbol | string;
Types
ObservableInput
type ObservableInput<T> = ObservableLike<T> | Record<string | symbol, unknown>;
ObservableLike
interface ObservableLike<T> {
subscribe(observer: Observer<T> | ((value: T) => void)): Subscription | (() => void);
}
Observer
interface Observer<T> {
next?(value: T): void;
error?(err: unknown): void;
complete?(): void;
}
ReadableStore
interface ReadableStore<T> {
getState(): T;
subscribe(listener: (state: T, previous: T) => void): () => void;
}
Subscription
interface Subscription {
unsubscribe(): void;
}