core / const
signal
Creates a mutable source signal from any JavaScript value.
Creates a mutable source signal from any JavaScript value.
A signal is a reactive data unit that automatically notifies dependent computations when its value changes. Signals use a global variable-based dependency tracking system to establish relationships with effects.
Signature
export const signal = <T>(input: T): SourceSignal<T> => {
let _value = immut(input);
const _effects = new Set<SignalsEffect>();
/**
* Runs all registered effects when the signal's value changes.
*
* This function iterates through all effects and:
* 1. Skips effects marked for disposal (canDisposeNow = true)
* 2. Removes disposed effects from the set (lazy cleanup)
* 3. Executes remaining effects
*
* The lazy cleanup approach ensures that effects are removed on the next
* signal update after being disposed, rather than immediately.
*/
const runEffects = () => {
_effects.forEach((effect) => {
if (effect.canDisposeNow) {
_effects.delete(effect);
return;
}
effect();
});
};
/**
* Updates the signal's value and triggers all dependent effects.
*
* This function is used internally by the signal's setter and by
* type-specific mutation methods (for arrays and objects).
*
* @param newValue The new value to set
*/
const setValueAndRunEffects = (newValue: T): void => {
_value = newValue;
runEffects();
};
const baseObject: BaseSourceSignal<T> = {
type: "source-signal",
get value() {
// Automatic dependency tracking: if an effect is currently executing,
// register this effect as a dependency of this signal
if (_currentSignalEffect) _effects.add(_currentSignalEffect);
// Return a fresh copy of the immutable value
return newVal(_value);
},
set value(newValue: T) {
// Skip if value hasn't changed (prevents unnecessary effect runs)
// TODO: if using 'areValuesEqual' from immutjs would be expensive for large data, consider a more efficient equality check or always trigger effects
if (newValue === _value) return;
// Store new value immutably and trigger effects
setValueAndRunEffects(immut(newValue));
}
Type Parameters
- The type of value the signal holds
Parameters
input: Any JavaScript value to convert to a signal
Returns
A source signal with a value getter/setter. Arrays
include mutation methods and plain objects include
set().
Remarks
- Setting the same value does not trigger effects
- Effects are triggered synchronously and immediately upon value change
- Signal values are stored immutably via
@cyftech/immutjs - Object
set()performs a shallow merge - Array mutation methods create new arrays internally
Examples
// Primitive values
const count = signal(0);
count.value = 1;
console.log(count.value); // 1
// Object values with partial updates
const user = signal({ name: "John", age: 30 });
user.set({ age: 31 }); // Shallow merge
console.log(user.value); // { name: "John", age: 31 }
// Array values with mutation methods
const items = signal([1, 2, 3]);
items.push(4);
items.remove((item) => item % 2 === 0); // Custom method