core / const

signal

Creates a mutable source signal from any JavaScript value.

Source: src/_core/signal/source-signal.ts

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

See Also

  • effect - For registering functions to run when signal values change
  • derive - For creating read-only derived signals