Angular Signals: set(), update(), computed(), effect() and linkedSignal()
These APIs have different responsibilities:
signal() → Store reactive state
set/update → Change signal state
computed() → Derive read-only state
effect() → Run side effects when dependencies change
linkedSignal() → Writable state linked to other reactive state
1. How do you read and update a Signal?
What is it?
After creating a signal:
count = signal(0);
you can:
- Read it using
count() - Set a new value using
count.set(...) - Update it based on the current value using
count.update(...)
Full Simple Program
counter.ts
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
templateUrl: './counter.html'
})
export class Counter {
count = signal(0);
increment() {
this.count.update(value => value + 1);
}
decrement() {
this.count.update(value => value - 1);
}
setHundred() {
this.count.set(100);
}
reset() {
this.count.set(0);
}
}
counter.html
<h2>Count: {{ count() }}</h2>
<button (click)="increment()">+</button>
<button (click)="decrement()">-</button>
<button (click)="setHundred()">Set 100</button>
<button (click)="reset()">Reset</button>
Reading a Signal
Signals are read by calling them like a function:
this.count()
In HTML:
{{ count() }}
For example:
count = signal(10);
console.log(this.count());
Output:
10
Remember:
count → Signal object
count() → Current value
Updating with set()
Use set() when you know exactly what the new value should be.
this.count.set(100);
Flow:
count = 10
count.set(100)
↓
count = 100
Updating with update()
Use update() when the new value depends on the current value.
this.count.update(value => value + 1);
If:
count = 10
then:
Current value
↓
10
↓
value => value + 1
↓
11
set() vs update()
| Method | Purpose | Example |
|---|---|---|
() |
Read value | count() |
set() |
Replace value | count.set(10) |
update() |
Calculate from current value | count.update(x => x + 1) |
Key Points
- Call a signal to read it:
count(). - Use
set()for direct replacement. - Use
update()when the new value depends on the old value. - A signal created with
signal()is normally aWritableSignal.
2. What is computed()?
What is computed()?
computed() creates a read-only derived Signal whose value is calculated from other Signals.
For example:
price = signal(100);
quantity = signal(2);
total = computed(() => this.price() * this.quantity());
total depends on:
price
quantity
If either changes, Angular knows that the computed value may need to be recalculated.
Full Simple Program
product.ts
import { Component, computed, signal } from '@angular/core';
@Component({
selector: 'app-product',
standalone: true,
templateUrl: './product.html'
})
export class Product {
price = signal(100);
quantity = signal(2);
total = computed(() =>
this.price() * this.quantity()
);
increaseQuantity() {
this.quantity.update(value => value + 1);
}
changePrice() {
this.price.set(200);
}
}
product.html
<h2>Product</h2>
<p>Price: {{ price() }}</p>
<p>Quantity: {{ quantity() }}</p>
<p>Total: {{ total() }}</p>
<button (click)="increaseQuantity()">
Increase Quantity
</button>
<button (click)="changePrice()">
Change Price
</button>
Explanation
Initially:
price = 100
quantity = 2
Our computed signal:
total = computed(() =>
this.price() * this.quantity()
);
calculates:
100 × 2
↓
200
Therefore:
this.total()
returns:
200
Now suppose:
this.quantity.set(3);
Conceptually:
quantity
2 → 3
↓
total depends on quantity
↓
100 × 3
↓
300
The template now sees:
Total: 300
computed() is Read-Only
This is important.
You can read:
this.total()
But you cannot do:
this.total.set(500);
because total is a computed/read-only signal.
Think:
price ─────┐
├──→ computed() ──→ total
quantity ──┘
Writable Read-only
Another Common Example
firstName = signal('Syed');
lastName = signal('Mohamed');
fullName = computed(() =>
`${this.firstName()} ${this.lastName()}`
);
Template:
<h2>{{ fullName() }}</h2>
If:
this.firstName.set('Abdul');
then fullName() reflects the new derived value.
Key Points
computed()creates derived state.- It automatically tracks signals read during its computation.
- It is read-only.
- Its calculation is lazy and memoized.
- Use it when one value can be derived from other state.
- Don't manually synchronize derived values when
computed()can express the relationship.
signal() → source state
computed() → derived state
3. What is effect()?
What is effect()?
effect() runs side-effecting code whenever the Signals it reads change.
Example:
effect(() => {
console.log(this.count());
});
Angular tracks the signal read:
this.count()
When count changes, the effect can run again.
Full Simple Program
counter.ts
import {
Component,
effect,
signal
} from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
templateUrl: './counter.html'
})
export class Counter {
count = signal(0);
constructor() {
effect(() => {
console.log(
'Count changed:',
this.count()
);
});
}
increment() {
this.count.update(value => value + 1);
}
}
counter.html
<h2>Count: {{ count() }}</h2>
<button (click)="increment()">
Increment
</button>
Explanation
The effect is:
effect(() => {
console.log(
'Count changed:',
this.count()
);
});
Because the effect reads:
this.count()
Angular registers count as a dependency.
Conceptually:
effect()
│
└── reads count()
│
↓
dependency tracked
When:
this.count.update(value => value + 1);
the signal changes.
count
↓
changes
↓
effect executes
↓
console.log(...)
Console might show:
Count changed: 0
Count changed: 1
Count changed: 2
Count changed: 3
When should effect() be used?
Use effect() when a state change needs to cause a side effect.
Examples include:
- logging
- synchronizing with browser APIs
- updating non-reactive third-party libraries
- custom persistence/integration scenarios
For example:
effect(() => {
console.log(this.username());
});
Don't use effect() for derived state
Suppose we have:
price = signal(100);
quantity = signal(2);
Don't normally do this:
total = signal(0);
effect(() => {
this.total.set(
this.price() * this.quantity()
);
});
You're manually synchronizing state.
Prefer:
total = computed(() =>
this.price() * this.quantity()
);
So:
Need calculated value?
↓
computed()
Need side effect?
↓
effect()
This distinction is very important for interviews.
computed() vs effect()
computed() |
effect() |
|---|---|
| Creates derived value | Performs side effect |
| Returns a Signal | Does not represent derived state |
Read with () |
Runs automatically |
| Used for calculations | Used for external actions |
| Read-only derived state | Side-effect mechanism |
Example:
total = computed(() =>
this.price() * this.quantity()
);
vs:
effect(() => {
console.log(this.total());
});
Flow:
price ───────┐
↓
computed()
↓
total
↓
effect()
↓
console.log()
4. What is linkedSignal()?
What is linkedSignal()?
linkedSignal() creates a writable signal whose value is linked to other reactive state.
This is useful when:
- you need a value derived from another Signal,
- but unlike
computed(), you also need to modify that value manually, - and you want it to reset/recalculate when its source changes.
This is the main idea:
computed()
↓
Derived + Read-only
linkedSignal()
↓
Derived/linked + Writable
Simple Real-World Example
Suppose a user chooses a product category.
Each category has a default product.
Category
↓
Default selected product
But the user should also be able to manually choose another product.
That's a good use case for linkedSignal().
Full Simple Program
product-selector.ts
import {
Component,
linkedSignal,
signal
} from '@angular/core';
@Component({
selector: 'app-product-selector',
standalone: true,
templateUrl: './product-selector.html'
})
export class ProductSelector {
category = signal('Laptop');
selectedProduct = linkedSignal(() => {
if (this.category() === 'Laptop') {
return 'Dell';
}
return 'iPhone';
});
selectLaptopCategory() {
this.category.set('Laptop');
}
selectMobileCategory() {
this.category.set('Mobile');
}
selectHp() {
this.selectedProduct.set('HP');
}
selectSamsung() {
this.selectedProduct.set('Samsung');
}
}
product-selector.html
<h2>Product Selector</h2>
<p>Category: {{ category() }}</p>
<p>
Selected Product:
{{ selectedProduct() }}
</p>
<hr>
<button (click)="selectLaptopCategory()">
Laptop Category
</button>
<button (click)="selectMobileCategory()">
Mobile Category
</button>
<hr>
<button (click)="selectHp()">
Select HP
</button>
<button (click)="selectSamsung()">
Select Samsung
</button>
Explanation
We have a normal signal:
category = signal('Laptop');
Then:
selectedProduct = linkedSignal(() => {
if (this.category() === 'Laptop') {
return 'Dell';
}
return 'iPhone';
});
selectedProduct depends on:
category()
Initially:
category = Laptop
↓
linkedSignal()
↓
selectedProduct = Dell
So:
this.selectedProduct()
returns:
Dell
But linkedSignal() is Writable
This is the major difference from computed().
The user can manually change:
this.selectedProduct.set('HP');
Now:
category = Laptop
selectedProduct = HP
That's allowed.
With computed(), this would not be possible.
What happens when the dependency changes?
Suppose:
this.category.set('Mobile');
The linked signal's computation runs based on the new category.
category
Laptop
↓
Mobile
↓
linkedSignal recalculates
↓
selectedProduct
↓
iPhone
So the previous manually selected:
HP
is replaced according to the new source state.
This makes linkedSignal() particularly useful for dependent selections and resettable state.
computed() vs linkedSignal()
This is the easiest way to understand linkedSignal().
computed()
category = signal('Laptop');
product = computed(() =>
this.category() === 'Laptop'
? 'Dell'
: 'iPhone'
);
You can:
this.product();
But cannot:
this.product.set('HP');
because computed() is read-only.
linkedSignal()
category = signal('Laptop');
product = linkedSignal(() =>
this.category() === 'Laptop'
? 'Dell'
: 'iPhone'
);
You can read:
this.product();
and write:
this.product.set('HP');
But when the reactive source changes, the linked computation can establish a new value.
Comparison
| Feature | signal() |
computed() |
linkedSignal() |
effect() |
|---|---|---|---|---|
| Stores value | Yes | Derived value | Yes | No |
| Writable | Yes | No | Yes | N/A |
| Tracks dependencies | As source state | Yes | Yes | Yes |
| Creates derived state | No | Yes | Yes/linked | No |
Can use set() |
Yes | No | Yes | No |
Can use update() |
Yes | No | Yes | No |
| Side effects | No | No | No | Yes |
Overall Flow
A useful mental model is:
Angular Signals
│
┌───────────────┼────────────────┐
│ │ │
▼ ▼ ▼
signal() computed() effect()
│ │ │
▼ ▼ ▼
Store state Derive state Side effect
│ │
│ │
Writable Read-only
│
│
└───────────────┐
▼
linkedSignal()
│
▼
Linked/derived state
+
Writable
Key Points
Reading and Updating
count()
reads a Signal.
count.set(10);
directly replaces its value.
count.update(x => x + 1);
calculates a new value from the current value.
computed()
total = computed(
() => this.price() * this.quantity()
);
Use it for derived state.
It is read-only.
effect()
effect(() => {
console.log(this.count());
});
Use it for side effects when reactive dependencies change.
Don't use effects as the default mechanism for synchronizing derived state.
linkedSignal()
selected = linkedSignal(
() => this.defaultValue()
);
Use it when state is linked to reactive dependencies but must remain writable.
The most important distinction is:
signal() → Writable state
computed() → Read-only derived state
linkedSignal() → Writable linked/derived state
effect() → Side effect
Interview Questions and Answers
1. How do you read a Signal?
Call it like a function:
count()
2. How do you update a Signal?
Using set():
count.set(10);
or update():
count.update(x => x + 1);
3. What is the difference between set() and update()?
set() replaces the value directly:
count.set(100);
update() calculates the new value from the current value:
count.update(x => x + 1);
4. What is computed()?
computed() creates a read-only Signal whose value is derived from other reactive dependencies.
total = computed(
() => this.price() * this.quantity()
);
5. Is a computed Signal writable?
No.
This is invalid:
this.total.set(100);
A computed Signal is read-only.
6. What is effect()?
effect() runs side-effecting code in response to changes in the Signals it reads.
effect(() => {
console.log(this.count());
});
7. What is the difference between computed() and effect()?
computed() is for creating derived state.
fullName = computed(
() => `${this.firstName()} ${this.lastName()}`
);
effect() is for performing side effects.
effect(() => {
console.log(this.fullName());
});
8. What is linkedSignal()?
linkedSignal() creates writable state that is reactively linked to other state.
selectedProduct = linkedSignal(
() => this.defaultProduct()
);
Unlike computed(), its value can also be manually changed.
9. What is the difference between computed() and linkedSignal()?
The main difference is writability:
computed()
Derived + Read-only
linkedSignal()
Linked/Derived + Writable
For example:
const value = computed(() => source());
You cannot:
value.set(10);
But:
const value = linkedSignal(() => source());
allows:
value.set(10);
10. When should you use linkedSignal() instead of signal()?
Use signal() when the state is independent:
count = signal(0);
Use linkedSignal() when the state has a reactive relationship with another piece of state but still needs to be manually writable.
A common example is:
Country changes
↓
Default city changes
↓
But user can manually select another city
This is a strong use case for linkedSignal().
Interview Memory Shortcut
signal()
↓
"I need state."
computed()
↓
"I need a value calculated from other state."
effect()
↓
"I need to DO something when state changes."
linkedSignal()
↓
"I need state linked to other state,
but I must also be able to change it."
The most important distinction to remember is computed() vs linkedSignal(): both can derive from reactive state, but computed() is read-only while linkedSignal() is writable and can reset/recompute when its source dependencies change.