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.

Derived state
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.

App.tsx · current repository API
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.

Observe and clean up
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();