Signals
Introduction To the Basics of Signals
In the context of visual rendering, Signals are a way to represent dynamic content in a component. They are used to update the component’s output when the signal value changes with out rerendering (or rerunning) the component.
What is a Signal ?
Signals are simple subscribable objects that hold a value, that value can be changed over time. When a signal value changes any registered listeners will be notified with the new value.
On top of that basic concept, signals can be connected to the DOM elements or attributes, when the signal value changes the connected DOM element or attribute will be updated with the new value.
Creating a Signal
To create a signal, you can use the createSignal
function from the sig
package, for convenience, that function returns a tuple with the signal object and a function to update the signal value.
Its possible to create a signal with the signal
function as well, that function returns a signal object.
function createSignal<T>(value: T, options?: SignalOptions): [Signal<T>, Signal<T>["setValue"]]
function signal<T>(value: T, options?: SignalOptions): Signal<T>
Here is an example of how to create a signal and use it :
import { createSignal } from 'sig';
const [signal$, setSignal] = createSignal([1, 2, 3]);console.log(signal$()); // [1, 2, 3]
setSignal([1, 2, 3, 4]);console.log(signal$()); // [1, 2, 3, 4]
setSignal((prev) => [...prev, 5]);console.log(signal$()); // [1, 2, 3, 4, 5]
import { signal } from 'sig';
const signal$ = signal([1, 2, 3]);console.log(signal$()); // [1, 2, 3]
signal$.setValue([1, 2, 3, 4]);console.log(signal$()); // [1, 2, 3, 4]
signal$.setValue((prev) => [...prev, 5]);console.log(signal$()); // [1, 2, 3, 4, 5]
Connecting Signals
Signals can be connected to the DOM elements or attributes, when the signal value changes the connected DOM element or attribute will be updated with the new value.
Here is an example of signal connected to a text element and element attribute:
import { createSignal } from 'sig';
function App() { const [count$, setCount] = createSignal(0); const [imageSize$, setImageSize] = createSignal('150'); return (<div> <p>Count: {count$}</p> <button onClick={() => { setCount(count$() + 1); setImageSize(`${Number(imageSize$()) + 10}`); }}>Increment</button> <img width={imageSize$} height={imageSize$} src="..." /> </div>);}
1
- The signal count$
is connected to the text element, when the signal value changes the text will be updated.
2
- The signal imageSize$
is connected to the width
and height
attributes of the image element, when the signal value changes the image size will be updated.
Signals Reactivity
Lets see a bad example of signal usage:
import { createSignal } from 'sig';
function FiveClickButton() { const [count$, setCount] = createSignal(0); return (<div> <button className="bg-indigo-600 text-white px-4 py-2 rounded-md" disabled={count$() >= 5} ❌ onClick={() => setCount(count$() + 1)}> Increment clicks {count$} </button> </div>);}
Here the signal count$
is connected correctly to the button text, it will be updated as expected when the signal value changes.
But the button disabled
attribute is not connected to the signal, it simply receives the evaluated value of the expression count$() >= 5
when the component is first rendered, (which is false
in this case), and it will not be updated when the signal value changes.
To fix this issue, we need to connect the signal to the disabled
attribute, so the button will be disabled when the signal value is greater than or equal to 5.
import { createSignal } from 'sig';
function FiveClickButton() { const [count$, setCount] = createSignal(0); return (<div> <button className="bg-indigo-600 text-white px-4 py-2 rounded-md" disabled={count$() >= 5} disabled={count$.derive((count) => count >= 5)} ✅ onClick={() => setCount(count$() + 1)}> Increment clicks {count$} </button> </div>);}