signal()

Create a reactive value that can be read and updated.

const count = signal(initialValue, debugName?)

Parameters

ParameterTypeDescription
initialValue T The initial value for the signal
debugName string Optional. Names the signal in devtools and in what_signals output; without it the signal shows up as signal_42. The what_lint hint module-scope-signal-missing-name flags module-scope signals that omit it.

Returns

A signal object with the following methods:

MethodDescription
signal() Read the current value (creates subscription in reactive contexts)
signal.set(value) Set a new value
signal.set(fn) Update based on current value
signal(value) Callable write form, equivalent to signal.set(value)
signal(fn) Callable update form, equivalent to signal.set(fn)
signal.peek() Read value without creating a subscription
signal.subscribe(fn) Run fn(value) immediately with the current value, then again on every change. Returns an unsubscribe function.

Usage

Basic Usage

import { signal } from 'what-framework';

const count = signal(0);

// Read
console.log(count());  // 0

// Update
count.set(5);
console.log(count());  // 5

// Update with function
count.set(c => c + 1);
console.log(count());  // 6

In Components

Pass a thunk ({() => ...}) for anything that has to stay live. A bare {count()} is only reactive when what-compiler runs, because the compiler rewrites it to () => count(). On the automatic JSX runtime or with hand-written h() it is read once when the component runs and never updates. The thunk form works on every path.

function Counter() {
  const count = signal(0, 'count');

  return (
    <button onClick={() => count.set(c => c + 1)}>
      {() => `Count: ${count()}`}
    </button>
  );
}

With Objects

const user = signal({ name: 'Alice', age: 25 });

// Update - must create new reference
user.set(u => ({ ...u, age: 26 }));

With Arrays

const items = signal([1, 2, 3]);

// Add item
items.set(arr => [...arr, 4]);

// Remove item
items.set(arr => arr.filter(x => x !== 2));

Reading Without Subscribing

import { signal, effect } from 'what-framework';

const count = signal(0);
const multiplier = signal(2);

effect(() => {
  // Uses count but doesn't subscribe to multiplier
  const result = count() * multiplier.peek();
  console.log(result);
});

Notes

  • Signals should not be mutated directly. Use .set() or the callable form count(next) to update.
  • In JSX, wrap the read in a thunk so the binding stays live: {() => count()}. A bare {count()} is only rewritten into a thunk by what-compiler.
  • Use peek() when you need the value but don't want to create a subscription
  • For derived values that depend on other signals, use computed()