This document serves to layout the potential options for using Signals in Angular with NgRx libraries and/or introducing a package in NgRx based on Signals with some aspects of ComponentStore.
The implementation document for Signals in Angular can be found here
Signals in Angular introduces a primitive for reactivity that's not built on top of RxJS. There will be interopability with RxJS observables and Signals through bridge functions such as toSignal
and toObservable
.
With NgRx libraries being built on top of observables, we can immediately take advantage of Signals in Angular by using the bridge function to go between Signal and Observable. This will allow developers to transform and consume Observables from the NgRx Store as Signals.
We improve this experience by providing a selectSignal
method that takes a selector and returns a Signal of its value.
As an alternative, we could also add to this experience by providing fromStore
that takes a selector and returns a Signal of its value, and dispatcher
to dispatch actions to the Store.
The fromStore
and dispatcher
options would require that they be always be used within an injection context.
ComponentStore provides reactivity for local component state and side effects using observables. Integrating with Signals would provide interopability with ComponentStore APIs.
As with NgRx Store, a selecSignal
method would be added to the ComponentStore API.
There would also be a state
property added to the ComponentStore API to provide reading from the ComponentStore state using the computed
function.
A ComponentStore effect would also support receiving a Signal
as an input.
- Using a Signal as a source of an effect through interop
The Redux pattern with NgRx Store serves as a type of reactive primitive in Angular. With Angular providing a built-in reactive primitive, we can focus more on building around that primitive for more complex behavior such as:
Store
based on SignalsPackage name suggestions:
@ngrx/signals
@ngrx/signal-store
@ngrx/state
Main Goals:
Key Principles:
signalStore
Other function name suggestions:
store
createStore
createSignalStore
The signalStore
function acts as a pipe that accepts a sequence of store features. By using various store features, we can add state slices, computed state, updaters, effects, hooks, and DI configuration to the signal store.
withState
- accepts a dictionary of state slices, and converts each slice into a signal.withComputed
- accepts the previous state slices and computed properties as factory argument. Returns a dictionary of computed properties.In the previous example we saw default behavior - signalStore
returns a tuple of provide and inject functions that can be further used in the component. However, we can also provide a signal store at the root level or directly get its instance by passing the config object as the first argument of the signalStore
function.
With { providedIn: 'root' }
, signalStore
will return inject function as a result:
There is also an option to get the signal store instance as a result by using { useInjection: false }
. This covers the use-case when we want to create a store within a component:
update
functionThe update
function is used to update the signal store state. It accepts a sequence of partial state objects or update functions that partially updates the state. This provides the ability to define reusable and tree-shakeable updater functions that can be used in any signal store.
Examples:
withUpdaters
- provides the ability to add updaters to the signal store. Its factory accepts state slices, computed properties, previously defined updaters, and update
function as an input argument.withEffects
- provides the ability to add effects to the signal store. Its factory accepts state slices, computed properties, updaters, previously defined effects, and update
function as an input argument.withHooks
- provides the ability to add custom logic on signal store init and/or destroy. Hook factories also accept state slices, computed properties, updaters, and effects.rxEffect
The rxEffect
function is a similar API to ComponentStore.effect
. It provides the ability to manage asynchronous side effects by using RxJS. It returns a function that accepts a static value, signal, or observable as an input argument.
The rxEffect
function can be used with signalStore
as we saw above or completely independent. When used within the component injection context, it will clean up subscription on destroy.
The
rxEffect
function can be part of the@ngrx/signals
/@ngrx/state
package or@ngrx/signals/rxjs-interop
/@ngrx/state/rxjs
subpackage.
Examples:
Every store feature returns an object that contains following properties:
For example, we can define withCallState
feature in the following way:
We can also create a helper function (Image Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
createSignalStoreFeature
) to easily create more complex custom features.
This feature can be further used in any signal store that needs call state as follows:
This (sub)package should provide the following APIs:
withEntities
feature that will add entityMap
and ids
as state, and entities
(entity list) as computed propertysetOne
, setAll
, deleteOne
, deleteMany
, etc.Example:
withEntities
function can be also used multiple times for the same store in case we want to have multiple collections within the same store: