Shared state without the ceremony
A todo list shared across components: the list, a filter, derived counts, and actions to mutate it. Any component can read or change it.
In React, “shared state” means picking a pattern (Context + reducer? Zustand?)
and building the plumbing before you write a single feature. In Reactra it’s one
store block.
React — Context + useReducer (the no-extra-deps baseline)
Section titled “React — Context + useReducer (the no-extra-deps baseline)”import { createContext, useContext, useReducer, useMemo, type ReactNode } from "react"import { type Todo, type Filter, makeId } from "./todos"
type State = { items: Todo[]; filter: Filter }type Action = | { type: "add"; text: string } | { type: "toggle"; id: string } | { type: "selectFilter"; filter: Filter } | { type: "clearCompleted" }
const reducer = (s: State, a: Action): State => { switch (a.type) { case "add": { const t = a.text.trim() return t ? { ...s, items: [...s.items, { id: makeId(), text: t, done: false }] } : s } case "toggle": return { ...s, items: s.items.map((i) => (i.id === a.id ? { ...i, done: !i.done } : i)) } case "selectFilter": return { ...s, filter: a.filter } case "clearCompleted": return { ...s, items: s.items.filter((i) => !i.done) } }}
const TodoCtx = createContext<{ state: State; dispatch: React.Dispatch<Action> } | null>(null)
export function TodoProvider({ children }: { children: ReactNode }) { const [state, dispatch] = useReducer(reducer, { items: [], filter: "all" }) return <TodoCtx.Provider value={{ state, dispatch }}>{children}</TodoCtx.Provider>}
export function useTodos() { const ctx = useContext(TodoCtx) if (!ctx) throw new Error("useTodos must be used inside <TodoProvider>") return ctx}
// …and every consumer re-derives:function Footer() { const { state, dispatch } = useTodos() const remaining = useMemo(() => state.items.filter((t) => !t.done).length, [state.items]) return ( <footer> <span>{remaining} left</span> <button onClick={() => dispatch({ type: "clearCompleted" })}>Clear completed</button> </footer> )}Plus: wrap the tree in <TodoProvider>, and every consumer does
useTodos() + dispatch({ type: … }) + its own useMemo for derived values.
Reactra — one store, consumed by name
Section titled “Reactra — one store, consumed by name”import { type Todo, type Filter, makeId } from "./todos"
session store todoStore { state items: Todo[] = [] state filter: Filter = "all"
derived remaining = items.filter((t) => !t.done).length derived visible = ( filter === "active" ? items.filter((t) => !t.done) : filter === "completed" ? items.filter((t) => t.done) : items )
action add(text: string) { const t = text.trim() if (t) items = [...items, { id: makeId(), text: t, done: false }] } action toggle(id: string) { items = items.map((i) => i.id === id ? { ...i, done: !i.done } : i) } action selectFilter(next: Filter) { filter = next } action clearCompleted() { items = items.filter((t) => !t.done) }}// any consumer — no provider, no hook to write, no dispatchexport component Footer { inject store todoStore { remaining, clearCompleted } view { <footer> <span>{remaining} left</span> <button onClick={clearCompleted}>Clear completed</button> </footer> }}What disappeared
Section titled “What disappeared”- The action-type union + the reducer +
dispatch({ type })→ actions are methods that mutate:items = [...items, todo] createContext+<TodoProvider>+ theuseTodoshook →inject store todoStore { … }by name- Per-consumer
useMemofor derived →derivedlives in the store, computed once - Wrapping the tree in a provider →
session storedeclares its own lifetime
session is the scope: the list lives for the page session. Swap it for route store (per-route) or export store (app-wide singleton) — the keyword is the
lifetime, no provider placement to reason about.
It’s not a new runtime
Section titled “It’s not a new runtime”The store compiles to a closure-based factory registered in a small
StoreRegistry; inject store becomes useReactraStore("todoStore") backed by
useSyncExternalStore — React’s own external-store hook. No Context, no
provider, no proxy.
Shared state is a declaration, not a plumbing project.