Pulse.Store
Pulse.Store is a reactive key-value store shared across all components. Use it when state genuinely belongs to more than one component and a signal on one component would create awkward coupling.
When to use it vs a signal
| Use a signal | Use Pulse.Store |
|---|---|
| State belongs to one component | State has no clear single owner |
| Only that component changes it | Multiple components read AND write it |
| Other components only read it | Multiple components need to react to changes |
If you're accessing OtherComponent.signal() from 4 different components, that's often a sign the value should be in Pulse.Store.
API
-- Define a key with a default (call once per key)
Pulse.Store.define("GlobalTarget", nil)
-- Read the current value
local target = Pulse.Store.get("GlobalTarget")
-- Write a new value (triggers all watchers)
Pulse.Store.set("GlobalTarget", someModel)
-- React to changes
Pulse.Store.watch("GlobalTarget", function(newValue)
-- called every time the value changes
end)
-- Get the raw Signal object
local sig = Pulse.Store.signal("GlobalTarget")
sig:onChange(function(v) ... end)
Defining keys
Call Pulse.Store.define in component setup. Define each key once — usually in the component that conceptually owns it:
defineComponent('Targeter', () => {
Pulse.Store.define('GlobalTarget', null)
Pulse.Store.define('ScanMode', 'Normal')
// ...
})
Practical example
Two components — Targeter locks onto a target, NapeSize reads the locked target to expand its hitbox:
// Targeter component
defineComponent('Targeter', () => {
Pulse.Store.define('LockedTarget', null)
function lockNearest() {
const t = Pulse.Aim.findNearest(...)
Pulse.Store.set('LockedTarget', t)
}
return [button('Lock Nearest', lockNearest)]
})
// NapeSize component — reads the locked target
defineComponent('NapeSize', () => {
const loop = Pulse.Loop.new(1.0, () => {
const target = Pulse.Store.get('LockedTarget')
for (const titan of func.GetCachedTitans()) {
const isTarget = (titan === target)
// apply bigger size if this is the locked target
}
})
// ...
})
NapeSize never imports or depends on Targeter — it just reads the store key. Either component can be removed without breaking the other.
Limitations
- Keys must be defined before they're used. Calling
geton an undefined key returns nil with no warning. watchcallbacks run synchronously whensetis called — same rules as signals (don't yield).- There's no schema or type checking. Conventions (always a Model, always a string) are up to you.