computed
A memoized read-only value that automatically updates when dependencies change.
API
function computed<T>(computedFn: (previousValue?: T) => T): () => T
computedFn
: A function that computes the value. It receives the previously computed value as an optional argument.- Returns: A read-only value.
Basic Usage
computed
values update automatically when any of their dependencies change.
import { signal, computed } from '@hellajs/core';
const firstName = signal("John");
const lastName = signal("Doe");
const fullName = computed(() => `${firstName()} ${lastName()}`);
console.log(fullName()); // "John Doe"
firstName("Jane");
console.log(fullName()); // "Jane Doe"
Key Concepts
Result Comparison
A deep equality check prevents propagation to dependents when the computed result remains unchanged, avoiding unnecessary effect executions.
import { signal, computed, effect } from '@hellajs/core';
const items = signal([1, 2, 3]);
const doubled = computed(() => {
console.log('Computing doubled...');
return items().map(x => x * 2); // Creates new array each time
});
effect(() => {
console.log('Effect:', doubled());
});
// This will trigger the computation and effect
items([2, 4, 6]); // Logs: "Computing doubled..." then "Effect: [4, 8, 12]"
// This will recompute but NOT trigger the effect (same result)
items([2, 4, 6]); // Logs: "Computing doubled..." only (no effect)
// The computed detects that [4, 8, 12] equals the previous [4, 8, 12]
// so dependent effects don't run unnecessarily
Computation Chaining
Computed values can depend on other computed values, creating an efficient graph of reactive dependencies.
import { signal, computed } from '@hellajs/core';
const price = signal(100);
const quantity = signal(2);
const subtotal = computed(() => price() * quantity());
const tax = computed(() => subtotal() * 0.07); // 7% tax
const total = computed(() => subtotal() + tax());
console.log(total()); // 214
quantity(3);
console.log(total()); // 321
Conditional Dependencies
A computed value only depends on the signals that are actually read during the last execution.
import { signal, computed } from '@hellajs/core';
const showAdvanced = signal(false);
const basic = signal('basic');
const advanced = signal('advanced');
const setting = computed(() => {
if (showAdvanced()) {
return `Advanced: ${advanced()}`;
}
return `Basic: ${basic()}`;
});
// Initially, `setting` only depends on `showAdvanced` and `basic`.
// Changing `advanced` will not trigger a re-computation.
advanced('new advanced');
console.log(setting()); // "Basic: basic"
// After this, `setting` depends on `showAdvanced` and `advanced`.
showAdvanced(true);
console.log(setting()); // "Advanced: new advanced"
Previous Value
The computedFn
function receives the previously computed value as its first argument, enabling incremental calculations and optimizations.
import { signal, computed } from '@hellajs/core';
const items = signal([]);
// Store count outside computed to track state
let lastCount = 0;
const total = computed((previousTotal = 0) => {
const currentItems = items();
// Only process new items since last computation
const newItems = currentItems.slice(lastCount);
const additionalSum = newItems.reduce((sum, item) => sum + item.value, 0);
lastCount = currentItems.length;
return previousTotal + additionalSum;
});
Important Considerations
Keep Computations Pure
The computedFn
function should not have side effects. Use effect
for side effects.
// ❌ Side effects in computed
const result = computed(() => {
fetch('/api/data');
return count() * 2;
});
// ✅ Pure computation
const doubled = computed(() => count() * 2);