Angular Signals

MD
R
Markdown

Angular Signals - New Reactive Primitive for a more simple and explicit reactivity on templates (from Solid.js)

Angular Signals

  • Reactive primitive > addition to the framework [Mechanism that can react to changes in the application’s state and automatically update its own state and view ]

https://stackblitz.com/edit/angular-ednkcj?file=src/main.ts

They provide one simple model to update Angular’s views.

With signals there is one simple mode to update the view: If you change the signal, the view will update. Which values trigger a view update is now explicitly stated, directly in the code.

A signal will return the value, but also register the signal as a dependency of the context in question

This is what makes signals so powerful as a mechanism of change detection and DOM synchronization. The affected template is directly notified. No walking of component trees and guessing when to re-check everything is necessary!*

* Reactive forms: A form component that uses reactive programming to manage its state and update its view as the user interacts with it.
* Reactive services: A service component that uses reactive programming to manage shared state between different parts of the application and notify components of changes in the state.
* Reactive components: A component that uses reactive programming to manage its own state and update its view as needed, without relying on input or output properties.
  • Signals: They contain values that change over time; when you change a signal’s value, it automatically updates anything that uses it.*
    • A signal is a wrapper around a simple value that registers what depends on that value and notifies those dependents whenever its value changes.
  • To enable its automatic change detection Angular depends on ZoneJs.
    • ZoneJs is a library that patches native browser APIs and notifies the framework whenever a significant event occurs.
  • Angular tends to overcheck and do unnecessary work trying to detect changes to the model.
  • Logical data flow of an application does not fit into the top-to-bottom nature of Angular’s change detection
  • onPush: super informative video https://www.youtube.com/watch?v=36G-ZFcllkk
  • The OnPush change detection strategy excludes the component marked as OnPush and all its children from the default change detection mechanism. https://blog.angular-university.io/onpush-change-detection-how-it-works/
  • If one of your components high up in the tree is OnPush, all other components will have to support OnPush also.
  • Angular uses RxJs for events, more specifically to expose streams of events.
  • RxJs’s BehaviorSubject is the closest thing the library offers to a signal. It always has a value
  • https://dev.to/modderme123/super-charging-fine-grained-reactive-performance-47ph
  • signals only notify consumers of their changes if their value changes.
  • Rxjs: You can listen to an input’s change events, debounce its values, switch them to parameters for an HTTP call, and map the response to the exact model that you need in the view, all in one place. This is simply not possible with signals.
  • RxJs shines by letting developers declare asynchronous, reactive streams
  • While both are reactive by nature, RxJs and signals solve different issues
  • I changed my mind. Angular needs a reactive primitive - DEV Community

Angular Signals

Angular Signals are zero-argument functions (() => T). When executed, they return the current value of the signal. Executing signals does not trigger side effects, though it may lazily recompute intermediate values (lazy memoization).

  • The signal() function produces a specific type of signal known as a SettableSignal. Note: We do not need immutability for signals to correctly notify their dependents of changes!!
// Example 1
const counter = signal(0);
counter.set(2);
counter.update(count => count + 1);

// Example 2
const todoList = signal<Todo[]>([]);
todoList.mutate(list => {
    list.push({title: 'One more task', completed: false});
});

// Example 3
const counter = signal(0);
// Automatically updates when `counter` changes:
const isEven = computed(() => counter() % 2 === 0); // memoizing signal - a signal can be used to create a cache for a function's results

// Example 4 (Side effects: effect())
const counter = signal(0);
effect(() => console.log('The counter is:', counter()));
// The counter is: 0
counter.set(1);
// The counter is: 1

// Example 5 (Untracked)
const counter0 = signal(0);
const counter1 = signal(0);
// Executes when `counter0` changes, not when `counter1` changes:
effect(() => console.log(counter0(), untracked(counter1));
counter0.set(1);
// logs 1 0
counter1.set(1);
// does not log
counter1.set(2);
// does not log
counter1.set(3);
// does not log
counter0.set(2);
// logs 2 3


angular/packages/core/src/signals at main · angular/angular · GitHub

Usage in Angular

Producer (values) Consumer (consume values / reactivity)

Together, they build a dependency graph

Phase 1: Producer PUSH to Consumer *"dirtiness" is eagerly pushed through the graph when a source signal is changed; informaiton only

Phase 2: Consumer PULL from Producer *recalculation is performed lazily, only when values are pulled by reading their signals.


      Producer
         |
         v
   +-----+-----+
   |           |
Consumer 1  Consumer 2
   ^           ^
   |           |
  pull()    pull()



const counter = signal(0);
const doubleCounter = computed(() => counter() * 2);

// an effect is a consumer
effect(() => renderTemplate(`
  <div>My counter is: ${counter()}</div>
  <div>My double counter is ${doubleCounter()}</div>
`));

Fine grained reactivity = fine grained change detection = performance explosion

signals + RxJs = <3

Changes

The async pipe will be replaced by a signal. OnPush will completely disappear as signals notify the framework whenever a DOM update is necessary. There will definitely be a way for signal and zone based change detection to co exist in the same application. Standalone components (making modules optional) Compare that to Angular where just running console.log() in ngDoCheck() to see how often change detection runs for no reason can give you shivers. With Signals, from what I gather, the only way to “subscribe” to changes is to use “effect()”. I think mixing reactive and non reactive variables in a reactive context is one of those things Signals will be officially part of the v16 release later this year.

https://analogjs.org/

Solid.js (powered by signals)

Solid.js is built around the concept of reactive programming, which allows components to react to changes in the application’s state automatically. This means that when the application’s state changes, Solid.js will automatically re-render the relevant components, without the need for manual updates or re-renders.

Created on 3/14/2023