Reactive state & context
Use Solid’s tracking model directly. Derive values with accessors and memos; add context when passing a store through a component tree helps.
Track the property you read
store.state is a Solid store proxy. JSX expressions, createMemo, and effects track reactive property reads. A function accessor preserves a future read; a primitive copied out of the proxy does not. Handler mutation is applied through Solid produce, rather than by replacing the entire state tree for each event.
const value = () => store.state.counter.value;
const doubled = createMemo(() => value() * 2);
// Snapshot only: this primitive will not track later changes.
const { value: snapshot } = store.state.counter;
return <p>{value()} / {doubled()}</p>;Use context when you need it
A provider is optional. Components can receive a store as a prop or close over an owned store. SoluxProvider passes a store to descendants; useSolux<State>() returns that store or undefined when no provider exists. Give useSolux the state type and handle a missing provider explicitly.
import { SoluxProvider, useSolux } from "@carere/solux";
import { onCleanup } from "solid-js";
import { createCounterStore, increment } from "./counter";
type State = ReturnType<typeof createCounterStore>["state"];
function CounterButton() {
const store = useSolux<State>();
if (!store) throw new Error("CounterButton requires SoluxProvider");
return <button onClick={() => store.dispatch(increment(1))}>
{store.state.counter.value}
</button>;
}
export function App() {
const store = createCounterStore();
onCleanup(() => store.dispose()); // Explicit, unreleased API.
return <SoluxProvider store={store}><CounterButton /></SoluxProvider>;
}Observe events deliberately
subscribe(listener) receives { state, event } after dispatch. A live subscription immediately replays the latest event if one exists; a new store has no initial event to replay. subscribeToEvent(creator, listener) filters that same stream by type: it replays only when the stream’s single latest event matches. Dispatching A, then B, then subscribing to A does not replay A. Both return RxJS subscriptions: call .unsubscribe() when finished.
The callback’s state is the live reactive state, not a historical snapshot. Copy serializable data if you keep a history. An unrelated event may notify subscribers even when no handler changes state.
const subscription = store.subscribe(({ state, event }) => {
console.log(event.type, state.counter.value);
});
const increments = store.subscribeToEvent(increment, ({ event }) => {
console.log(event.payload); // Inferred number.
});
subscription.unsubscribe();
increments.unsubscribe();