mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
docs: add section about reactive contexts
(cherry picked from commit 62ccd64e9f)
This commit is contained in:
parent
0e59ac795c
commit
dc0aae9a59
4 changed files with 65 additions and 37 deletions
|
|
@ -230,7 +230,7 @@ export class MediaControls {
|
|||
|
||||
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.
|
||||
|
||||
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`.
|
||||
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`.
|
||||
|
||||
See [Two-way binding](guide/templates/two-way-binding) for more details on two-way binding in templates.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ A component can define **queries** that find child elements and read values from
|
|||
Developers most commonly use queries to retrieve references to child components, directives, DOM elements, and more.
|
||||
|
||||
All query functions return signals that reflect the most up-to-date results. You can read the
|
||||
result by calling the signal function, including in reactive contexts like `computed` and `effect`.
|
||||
result by calling the signal function, including in [reactive contexts](guide/signals#reactive-contexts) like `computed` and `effect`.
|
||||
|
||||
There are two categories of query: **view queries** and **content queries.**
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,68 @@ If you set `showCount` to `true` and then read `conditionalCount` again, the der
|
|||
|
||||
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`.
|
||||
|
||||
## Reactive contexts
|
||||
|
||||
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_.
|
||||
|
||||
Angular automatically enters a reactive context when:
|
||||
|
||||
- Executing an `effect`, `afterRenderEffect` callback.
|
||||
- Evaluating a `computed` signal.
|
||||
- Evaluating a `linkedSignal`.
|
||||
- Evaluating a `resource`'s params or loader function.
|
||||
- Rendering a component template (including bindings in the [host property](guide/components/host-elements#binding-to-the-host-element)).
|
||||
|
||||
During these operations, Angular creates a _live_ connection. If a tracked signal changes, Angular will _eventually_ re-run the consumer.
|
||||
|
||||
### Asserts the reactive context
|
||||
|
||||
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.
|
||||
|
||||
```ts
|
||||
import { assertNotInReactiveContext } from '@angular/core';
|
||||
|
||||
function subscribeToEvents() {
|
||||
assertNotInReactiveContext(subscribeToEvents);
|
||||
// Safe to proceed - subscription logic here
|
||||
}
|
||||
```
|
||||
|
||||
### Reading without tracking dependencies
|
||||
|
||||
Rarely, you may want to execute code which may read signals within a reactive function such as `computed` or `effect` _without_ creating a dependency.
|
||||
|
||||
For example, suppose that when `currentUser` changes, the value of a `counter` should be logged. You could create an `effect` which reads both signals:
|
||||
|
||||
```ts
|
||||
effect(() => {
|
||||
console.log(`User set to ${currentUser()} and the counter is ${counter()}`);
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
You can prevent a signal read from being tracked by calling its getter with `untracked`:
|
||||
|
||||
```ts
|
||||
effect(() => {
|
||||
console.log(`User set to ${currentUser()} and the counter is ${untracked(counter)}`);
|
||||
});
|
||||
```
|
||||
|
||||
`untracked` is also useful when an effect needs to invoke some external code which shouldn't be treated as a dependency:
|
||||
|
||||
```ts
|
||||
effect(() => {
|
||||
const user = currentUser();
|
||||
untracked(() => {
|
||||
// If the `loggingService` reads signals, they won't be counted as
|
||||
// dependencies of this effect.
|
||||
this.loggingService.log(`User set to ${user}`);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Advanced derivations
|
||||
|
||||
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
|
|||
isWritableSignal(doubled); // false
|
||||
```
|
||||
|
||||
### Reading without tracking dependencies
|
||||
|
||||
Rarely, you may want to execute code which may read signals within a reactive function such as `computed` or `effect` _without_ creating a dependency.
|
||||
|
||||
For example, suppose that when `currentUser` changes, the value of a `counter` should be logged. You could create an `effect` which reads both signals:
|
||||
|
||||
```ts
|
||||
effect(() => {
|
||||
console.log(`User set to ${currentUser()} and the counter is ${counter()}`);
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
You can prevent a signal read from being tracked by calling its getter with `untracked`:
|
||||
|
||||
```ts
|
||||
effect(() => {
|
||||
console.log(`User set to ${currentUser()} and the counter is ${untracked(counter)}`);
|
||||
});
|
||||
```
|
||||
|
||||
`untracked` is also useful when an effect needs to invoke some external code which shouldn't be treated as a dependency:
|
||||
|
||||
```ts
|
||||
effect(() => {
|
||||
const user = currentUser();
|
||||
untracked(() => {
|
||||
// If the `loggingService` reads signals, they won't be counted as
|
||||
// dependencies of this effect.
|
||||
this.loggingService.log(`User set to ${user}`);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Using signals with RxJS
|
||||
|
||||
See [RxJS interop with Angular signals](ecosystem/rxjs-interop) for details on interoperability between signals and RxJS.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {RuntimeError, RuntimeErrorCode} from '../../errors';
|
|||
* to disallow certain code from running inside a reactive context (see {@link /api/core/rxjs-interop/toSignal toSignal})
|
||||
*
|
||||
* @param debugFn a reference to the function making the assertion (used for the error message).
|
||||
* @see [Asserts the reactive context](guide/signals#asserts-the-reactive-context)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue