Angular Signals: linkedSignal vs computed
Problem Statement
In Angular Signals, you have multiple tools to manage reactive state: signal
, computed
, and the newer linkedSignal
. When deriving state from other signals, why choose linkedSignal
instead of computed
? The confusion arises when managing values derived from another signal (like a selection that depends on a dynamic list of options) that also need explicit local updates.
Consider a scenario with a list of shipping options and a selected option:
const shippingOptions = signal(['Ground', 'Air', 'Sea']);
// Why not always use computed?
const selectedOption = computed(() => shippingOptions()[0]);
The key dilemma: computed
works for pure derivations, but fails when you need:
- Direct write access to the derived value
- Custom logic to handle source data changes
- State consistency when underlying data changes
Solution: When to Use linkedSignal
Key Conceptual Differences
computed | linkedSignal | |
---|---|---|
Mutability | Read-only | Writable |
Dependencies | Auto-tracked | Explicitly defined |
Update Control | Recalculated on every dependency change | Customizable recalculation logic |
Previous Value Access | No | Yes |
Best For | Pure derivations | Local mutable state with external dependencies |
When linkedSignal Outperforms computed
Use linkedSignal
when you need:
- Local writable state that depends on external signals
- Fallback logic when source data changes
- Manual updates to derived values
Basic Usage Pattern
const shippingOptions = signal(['Ground', 'Air', 'Sea']);
const selectedOption = linkedSignal(
() => shippingOptions()[0] // Default derivation
);
// Unlike computed, you can update it:
selectedOption.set('Air');
Practical Implementation
Scenario: Dynamic List Selection
const animals = signal(['Cat', 'Dog', 'Pig']);
// Without linkedSignal (problematic implementation)
const selectedAnimal = signal(animals()[0]);
removeAnimal('Cat') {
animals.update(list => list.filter(a => a !== 'Cat'));
// Problem: selectedAnimal still points to 'Cat'
}
SELECTION CORRUPTION RISK
When the source list changes, selections referencing missing items become invalid. linkedSignal
solves this automatically.
Improved linkedSignal Implementation
const selectedAnimal = linkedSignal<string[], string>({
source: animals,
computation: (newList, previousValue) => {
// On initial run
if (!previousValue) return newList[0];
// If previous selection exists, keep it
if (newList.includes(previousValue.value)) {
return previousValue.value;
}
// Fallback to first item
return newList[0];
}
});
Comparison With Other Approaches
Approach | Example Scenario | Limitation |
---|---|---|
computed | Derived read-only value | No write capability |
signal + manual sync | Simple selection | Complex synchronization |
linkedSignal | Selection in dynamic lists | Native value consistency |
Use Case Breakdown
Typical linkedSignal Applications
Item Selection in CRUD Interfaces
Automatically update selections when items are deletedConfigurable Default Values
typescriptconst defaultTheme = signal('light'); const userTheme = linkedSignal(() => defaultTheme()); // User can override temporarily userTheme.set('dark');
Contextual State Preservation
Maintain selections during data refreshes when possible
DESIGN PATTERN
Think of linkedSignal
as a reactive two-way bridge:
- Top-down: Reacts to source signal changes
- Bottom-up: Allows local overrides
Advanced: Controlled Computation Pattern
For complex state reconciliation:
linkedSignal({
source: [signalA, signalB], // Multiple sources
computation: ([aVal, bVal], prev) => {
if (customCondition(aVal, bVal, prev?.value)) {
return customValue;
}
return fallbackValue;
}
})
Key Takeaways
- Use
computed
for pure derivations without write requirements - Use
linkedSignal
for mutable derived state that requires:- Write capabilities
- Automatic source change handling
- Custom fallback logic
- Always provide value validation in computation logic to prevent stale references
Implement linkedSignal
whenever managing user selections, configurable defaults, or any state needing synchronization with source data while allowing local override. In standard derivation scenarios without write requirements, computed
remains the simpler choice.