Skip to content

Commit 62ccd64

Browse files
SkyZeroZxAndrewKushnir
authored andcommitted
docs: add section about reactive contexts
1 parent dc7ae9a commit 62ccd64

File tree

4 files changed

+65
-37
lines changed

4 files changed

+65
-37
lines changed

adev/src/content/guide/components/inputs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ export class MediaControls {
230230

231231
In the above example, the `CustomSlider` can write values into its `value` model input, which then propagates those values back to the `volume` signal in `MediaControls`. This binding keeps the values of `value` and `volume` in sync. Notice that the binding passes the `volume` signal instance, not the _value_ of the signal.
232232

233-
In other respects, model inputs work similarly to standard inputs. You can read the value by calling the signal function, including in reactive contexts like `computed` and `effect`.
233+
In other respects, model inputs work similarly to standard inputs. You can read the value by calling the signal function, including in [reactive contexts](guide/signals#reactive-contexts) like `computed` and `effect`.
234234

235235
See [Two-way binding](guide/templates/two-way-binding) for more details on two-way binding in templates.
236236

adev/src/content/guide/components/queries.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A component can define **queries** that find child elements and read values from
77
Developers most commonly use queries to retrieve references to child components, directives, DOM elements, and more.
88

99
All query functions return signals that reflect the most up-to-date results. You can read the
10-
result by calling the signal function, including in reactive contexts like `computed` and `effect`.
10+
result by calling the signal function, including in [reactive contexts](guide/signals#reactive-contexts) like `computed` and `effect`.
1111

1212
There are two categories of query: **view queries** and **content queries.**
1313

adev/src/content/guide/signals/overview.md

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,68 @@ If you set `showCount` to `true` and then read `conditionalCount` again, the der
8989

9090
Note that dependencies can be removed during a derivation as well as added. If you later set `showCount` back to `false`, then `count` will no longer be considered a dependency of `conditionalCount`.
9191

92+
## Reactive contexts
93+
94+
A **reactive context** is a runtime state where Angular monitors signal reads to establish a dependency. The code reading the signal is the _consumer_, and the signal being read is the _producer_.
95+
96+
Angular automatically enters a reactive context when:
97+
98+
- Executing an `effect`, `afterRenderEffect` callback.
99+
- Evaluating a `computed` signal.
100+
- Evaluating a `linkedSignal`.
101+
- Evaluating a `resource`'s params or loader function.
102+
- Rendering a component template (including bindings in the [host property](guide/components/host-elements#binding-to-the-host-element)).
103+
104+
During these operations, Angular creates a _live_ connection. If a tracked signal changes, Angular will _eventually_ re-run the consumer.
105+
106+
### Asserts the reactive context
107+
108+
Angular provides the `assertNotInReactiveContext` helper function to assert that code is not executing within a reactive context. Pass a reference to the calling function so the error message points to the correct API entry point if the assertion fails. This produces a clearer, more actionable error message than a generic reactive context error.
109+
110+
```ts
111+
import { assertNotInReactiveContext } from '@angular/core';
112+
113+
function subscribeToEvents() {
114+
assertNotInReactiveContext(subscribeToEvents);
115+
// Safe to proceed - subscription logic here
116+
}
117+
```
118+
119+
### Reading without tracking dependencies
120+
121+
Rarely, you may want to execute code which may read signals within a reactive function such as `computed` or `effect` _without_ creating a dependency.
122+
123+
For example, suppose that when `currentUser` changes, the value of a `counter` should be logged. You could create an `effect` which reads both signals:
124+
125+
```ts
126+
effect(() => {
127+
console.log(`User set to ${currentUser()} and the counter is ${counter()}`);
128+
});
129+
```
130+
131+
This example will log a message when _either_ `currentUser` or `counter` changes. However, if the effect should only run when `currentUser` changes, then the read of `counter` is only incidental and changes to `counter` shouldn't log a new message.
132+
133+
You can prevent a signal read from being tracked by calling its getter with `untracked`:
134+
135+
```ts
136+
effect(() => {
137+
console.log(`User set to ${currentUser()} and the counter is ${untracked(counter)}`);
138+
});
139+
```
140+
141+
`untracked` is also useful when an effect needs to invoke some external code which shouldn't be treated as a dependency:
142+
143+
```ts
144+
effect(() => {
145+
const user = currentUser();
146+
untracked(() => {
147+
// If the `loggingService` reads signals, they won't be counted as
148+
// dependencies of this effect.
149+
this.loggingService.log(`User set to ${user}`);
150+
});
151+
});
152+
```
153+
92154
## Advanced derivations
93155

94156
While `computed` handles simple readonly derivations, you might find youself needing a writable state that is dependant on other signals.
@@ -148,41 +210,6 @@ isWritableSignal(count); // true
148210
isWritableSignal(doubled); // false
149211
```
150212

151-
### Reading without tracking dependencies
152-
153-
Rarely, you may want to execute code which may read signals within a reactive function such as `computed` or `effect` _without_ creating a dependency.
154-
155-
For example, suppose that when `currentUser` changes, the value of a `counter` should be logged. You could create an `effect` which reads both signals:
156-
157-
```ts
158-
effect(() => {
159-
console.log(`User set to ${currentUser()} and the counter is ${counter()}`);
160-
});
161-
```
162-
163-
This example will log a message when _either_ `currentUser` or `counter` changes. However, if the effect should only run when `currentUser` changes, then the read of `counter` is only incidental and changes to `counter` shouldn't log a new message.
164-
165-
You can prevent a signal read from being tracked by calling its getter with `untracked`:
166-
167-
```ts
168-
effect(() => {
169-
console.log(`User set to ${currentUser()} and the counter is ${untracked(counter)}`);
170-
});
171-
```
172-
173-
`untracked` is also useful when an effect needs to invoke some external code which shouldn't be treated as a dependency:
174-
175-
```ts
176-
effect(() => {
177-
const user = currentUser();
178-
untracked(() => {
179-
// If the `loggingService` reads signals, they won't be counted as
180-
// dependencies of this effect.
181-
this.loggingService.log(`User set to ${user}`);
182-
});
183-
});
184-
```
185-
186213
## Using signals with RxJS
187214

188215
See [RxJS interop with Angular signals](ecosystem/rxjs-interop) for details on interoperability between signals and RxJS.

packages/core/src/render3/reactivity/asserts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {RuntimeError, RuntimeErrorCode} from '../../errors';
1515
* to disallow certain code from running inside a reactive context (see {@link /api/core/rxjs-interop/toSignal toSignal})
1616
*
1717
* @param debugFn a reference to the function making the assertion (used for the error message).
18+
* @see [Asserts the reactive context](guide/signals#asserts-the-reactive-context)
1819
*
1920
* @publicApi
2021
*/

0 commit comments

Comments
 (0)