SprinculModel is the base class you extend to create reactive components. Every model you write inherits the $el property, the reactive state proxy, the two lifecycle hooks, and the addComputedProp helper. You never call new SprinculModel() yourself — Sprincul does that when it processes each data-model element during Sprincul.init().
Properties
$el
The DOM element that this model instance is bound to — the element carrying the data-model attribute. All bindings and event listeners managed by this model are scoped to descendants of $el.
Type: HTMLElement
state
A reactive proxy over the model’s internal nanostores MapStore. Read and write state as ordinary object properties. Every assignment to a state property triggers all data-bind-<prop> callbacks for that property and re-evaluates any computed properties that list it as a dependency.
Type: Record<string, any>
State property names must be lowercase. HTML lower-cases data-* attribute names when building the DOM, so a binding written as data-bind-myProp becomes data-bind-myprop — and Sprincul reads that lowercase attribute name to match against state keys. If you use mixed-case state keys your bindings will silently never fire.
Lifecycle hooks
Sprincul calls lifecycle hooks on each model instance duringSprincul.init(). You override them in your subclass; both are optional.
beforeInit()
Called synchronously before bindings are attached to the DOM. This is the right place to initialize state values and register computed properties with addComputedProp(). Because bindings haven’t been wired yet, you can freely set initial state without triggering renders.
Return type: void | Promise<void>
If you return a Promise, Sprincul logs any rejection but does not await it before wiring bindings. The async portion of beforeInit may finish after bindings are already active. For reliable async work, use afterInit() instead.
afterInit()
Called after bindings and event listeners are active. This hook can be safely async. Use it for API calls, connecting to external stores, or any work that should happen once the component is fully wired.
Return type: void | Promise<void>
When afterInit completes (including any async work), Sprincul removes the data-cloaked attribute from the model’s root element if one is present. This makes afterInit the right hook to drive model-level uncloaking.
Methods
addComputedProp(name, fn, dependencies)
Registers a derived state value that is recomputed whenever any of its listed dependencies change. The computed value is accessible on this.state.<name> just like any other state property, and any data-bind-<name> elements are re-rendered when the value changes.
Call addComputedProp from inside beforeInit(). Calling it before the internal core is ready will throw.
The state key under which the computed value is stored. Must be lowercase.
A function that returns the computed value. Called once immediately to seed the initial value, then again whenever a dependency changes.
An array of state property names that this computed property depends on. Sprincul re-runs
fn and schedules a DOM update whenever any listed key changes. If you pass an empty array, the value is set once but bound elements will not re-render when it changes — Sprincul logs a warning in this case.() => void — An unsubscribe function. You can call it to manually remove the computed property listener. Sprincul also cleans it up automatically when the model root is removed from the DOM.
Computed property values and their dependency updates are batched with
requestAnimationFrame, the same as regular state changes. Multiple updates in a single frame coalesce into one render pass.