On This Page
Signal Options
To provide more granular control over reactivity, Semantic provides several options which let you control how values are updated and compared.
The default options are designed to permit some minor performance overhead as a tradeoff for improved developer experience, but this might not fit all use-cases for Signals.
Signal options are passed in the second argument to the Signal constructor.
Equality Comparison
By default, Signals use a deep equality comparison, isEqual, to determine if a new value is actually different from the current value. This prevents Reactions from rerunning if the updated value has not changed.
import { Signal } from '@semantic-ui/reactivity';
const person = new Signal({ name: 'John', age: 30 });
// No reactive update triggered (objects are deep equal)person.set({ name: 'John', age: 30 });
// Reactive update triggered (objects differ)person.set({ name: 'Jane', age: 30 });Custom Equality Function
You can provide a custom equality comparison function using the equalityFunction option to modify how equality checks are performed. This can potentially speed up comparisons at the cost of potential additional reactivity.
When To Use Custom equality functions can be useful for extremely large objects or data structures where you want to avoid checking their values each time they are accessed.
const customEquality = (a, b) => { // Custom comparison logic (e.g., shallow compare, reference compare) return a === b; // Example: strict reference equality};
// Signal using custom equality checkconst customVar = new Signal(initialValue, { equalityFunction: customEquality });Safety Presets
Signals have three safety presets controlling how the stored value is protected against accidental mutation. Set the preset via the safety option.
| Preset | On set | On .get().x = y | Dedupe | Use case |
|---|---|---|---|---|
freeze (default) | deep-freeze plain objects and arrays | throws TypeError | isEqual | state your code owns end-to-end |
reference | store raw | silent (no reactivity) | isEqual | third-party objects, perf-critical paths |
none | store raw | silent (no reactivity) | never | event-stream semantics — every set notifies |
safety: 'freeze' — the default
The default deep-freezes object and array values when you call set(). Accidental in-place mutation throws at the call site instead of silently dropping the update.
const count = new Signal({ n: 0 });
count.get().n = 1; // TypeError — "Signal value is frozen — cannot set property `n`"
// Correct ways to update:count.set({ n: 1 }); // replace the whole valuecount.mutate(prev => ({ n: prev.n + 1 })); // return a new valuecount.setProperty('n', 1); // mutation helper — rebuilds immutably under freezeDeep-freeze only walks arrays and plain objects. Date, Map, Set, RegExp, DOM nodes, and class instances are stored by reference — their own mutation semantics are preserved.
safety: 'reference' — opt-out for borrowed data
Use reference when the signal holds objects you didn’t construct yourself. Freezing them would poison the lender’s internal references; see Signals and Foreign References for the full heuristic.
const searchResults = new Signal([], { safety: 'reference' });Direct mutation on .get() values fails silently under reference — the helpers (push, splice, setProperty) remain the safe update path.
safety: 'none' — event-stream semantics
Use none when every set should notify subscribers, even if the value is deeply equal to the previous one. Suitable for notification channels where the payload’s shape repeats.
const pulse = new Signal(null, { safety: 'none' });
pulse.set({ type: 'heartbeat' });pulse.set({ type: 'heartbeat' }); // still notifies, even though isEqual would say equalCustom Clone Function
The default cloneFunction is used by signal.clone() to produce a deep-mutable copy on demand. Override it if you need non-default clone semantics.
const jsonClone = (value) => JSON.parse(JSON.stringify(value));const mySignal = new Signal({ data: 1 }, { cloneFunction: jsonClone });