5.6 KiB
Global State Management: Atomic & Modular
SigPro leverages the native power and efficiency of signals to create robust global stores with zero complexity. While other frameworks force you into heavy libraries and rigid boilerplate (Redux, Pinia, or Svelte stores), SigPro treats “the store” as a simple architectural choice: defining a signal outside of a component.
Availability:
$(and other core functions) are exported from the SigPro module. In ESM you must import them (import { $ } from 'sigpro'). In the IIFE classic script,$is automatically available onwindow. The examples below assume$is already in scope (via import or global).
Modular Organization (Zero Constraints)
You are not restricted to a single store.js. You can organize your state by feature, domain, or page. Since a SigPro store is just a standard JavaScript module exporting signals, you can name your files whatever you like (auth.js, cart.js, settings.js) to keep your logic clean.
1. File‑Based Stores (<any-name>.js)
Creating a dedicated file allows you to export only what you need. This modularity ensures tree shaking works perfectly – you never load state that isn’t imported.
// auth.js
import { $ } from 'sigpro';
// A simple global signal
export const user = $({ name: "Guest", loggedIn: false });
// A persistent global signal (auto‑syncs with localStorage)
export const theme = $("light", "app-theme-pref");
// A computed global signal that reacts to the 'user' signal
export const welcomeMessage = $(() => `Welcome back, ${user().name}!`);
2. Cross‑Component Consumption
Once exported, these signals act as a single source of truth. SigPro ensures that if a signal changes in one file, every component importing it across the entire app updates atomically without a full re‑render.
// Profile.js
import { user } from "./auth.js";
const Profile = () => div([
h2(() => user().name),
button({ onclick: () => user({ name: "John Doe", loggedIn: true }) }, "Log In")
]);
// Navbar.js
import { welcomeMessage, theme } from "./auth.js";
const Navbar = () => nav({ class: () => theme() }, [
span(() => welcomeMessage())
]);
Why SigPro Stores Are Superior
| Feature | SigPro | Redux / Pinia / Svelte |
|---|---|---|
| Boilerplate | 0% (just a variable) | High (actions, reducers, stores) |
| Organization | Unlimited (any filename) | Often strictly “store” files |
| Persistence | Native (just add a key) | Requires middleware / plugins |
| Learning Curve | Instant | Steep / complex |
| Bundle Size | 0KB (part of core) | 10KB – 30KB+ |
The Persistence Advantage
The magic of SigPro’s $(value, "key") is that it works identically for local and global states. By simply adding a second argument, your modular store survives browser refreshes automatically. No manual localStorage.getItem or JSON.parse logic is ever required.
// This single line creates a global, reactive,
// and persistent store for a shopping cart.
export const cart = $([], "session-cart");
Summary of Scopes
| Scope | Definition | Behaviour |
|---|---|---|
| Local | Signal defined inside a component | Unique to every component instance |
| Module | Signal defined outside a component (same file) | Shared by all instances within that file |
| Global | Signal defined in a separate file and imported | Shared across the entire application |
| Persistent | Any Signal defined with a key (e.g., $([], "cart")) |
Shared globally and persisted in localStorage |
Complete Example – Todo Store
// store/todos.js
import { $ } from 'sigpro';
export const todos = $([], "todos");
export const filter = $("all");
export const addTodo = (text) => {
todos([...todos(), { id: Date.now(), text, done: false }]);
};
export const toggleTodo = (id) => {
todos(todos().map(t => t.id === id ? { ...t, done: !t.done } : t));
};
export const filteredTodos = $(() => {
const all = todos();
if (filter() === "active") return all.filter(t => !t.done);
if (filter() === "completed") return all.filter(t => t.done);
return all;
});
// components/TodoApp.js
import 'sigpro';
import { todos, filter, addTodo, toggleTodo, filteredTodos } from "../store/todos.js";
const TodoApp = () =>
div({ class: "todo-app" }, [
input({ placeholder: "Add todo...", onKeyDown: (e) => {
if (e.key === "Enter" && e.target.value) {
addTodo(e.target.value);
e.target.value = "";
}
}}),
div({ class: "filters" }, [
button({ onClick: () => filter("all") }, "All"),
button({ onClick: () => filter("active") }, "Active"),
button({ onClick: () => filter("completed") }, "Completed")
]),
ul(
each(filteredTodos,
(todo) => li([
input({ type: "checkbox", checked: () => todo.done, onInput: () => toggleTodo(todo.id) }),
span(() => todo.done ? s(todo.text) : todo.text)
]),
(todo) => todo.id
)
)
]);
mount(TodoApp, "#app");