signalSlice
signalSlice
is loosely inspired by the createSlice
API from Redux Toolkit. The general idea is that it allows you to declaratively create a “slice” of state. This state will be available as a readonly signal.
The key motivation, and what makes this declarative, is that all the ways for
updating this signal are declared upfront with sources
and actionSources
.
It is not possible to imperatively update the state.
Basic Usage
The returned state
object will be a standard readonly signal, but it will also have properties attached to it that will be discussed below.
You can access the state as you would with a typical signal:
However, by default computed
selectors will be created for each top-level property in the initial state:
Sources
One way to update state is through the use of sources
. These are intended to be used for “auto sources” — as in, observable streams that will emit automatically like an http.get()
. Although it will work with a Subject
that you next
as well, it is recommended that you use an actionSource for these imperative style state updates.
You can supply a source like this:
The source
should be mapped to a partial of the initialState
. In the example above, when the source emits it will update both the checklists
and the loaded
properties in the state signal.
If you need to utilise the current state in a source, instead of supplying the observable directly as a source you can supply a function that accepts the state signal and returns the source:
Action Sources
Another way to update the state is through actionSources
. An action source creates an action that you can call, and it returns a source that is used to update the state.
This is good for situations where you need to manually/imperatively trigger some action, and then use the current state in some way in order to calculate the new state.
When you supply an actionSource
, it will automatically create an action
that you can call. Action Sources can be created like this:
Actions are created automatically using whatever name you provide for the
actionSource
and can be called like this:
It is also possible to have an actionSource
without any payload. For example
sometimes people might want to manually trigger a load:
In this particular case, a load
action will be created that can be called with
this.state.load()
.
NOTE: This example covers the use case where data needs to be manually
triggered with a load()
action. It is also possible to just have your data
load automatically — in this case the observable that loads the data can just be
supplied directly through sources
rather than actionSources
and it will be
loaded automatically without needing to trigger the load()
action.
It is also possible to supply an external subject as an actionSource
like
this:
This is useful for circumstances where you need any of your sources
to react
to someAction$
being triggered. A source can not react to internally created
actionSources
, but it can react to the externally created subject. Supplying
this subject as an actionSource
allows you to still trigger it through
state.someAction()
. This makes using actions more consistent, as everything
can be accessed on the state object, even if you need to create an external
subject.
Action Updates
:::caution Action Updates are currently experimental, the API may be changed or removed entirely. Please feel free to reach out to joshuamorony with feedback or open an issue. :::
Each actionSource
will have an equivalent Updated
version signal automatically generated that will be incremented each time the actionSource
emits or completes, e.g:
This signal will return the current version, starting at 0
. If you do not want your effect
to be triggered on initialisation you can check for the 0
version value, e.g:
Action Streams
The source/stream for each action is also exposed on the state object. That means that you can access:
Which will allow you to react to the add
action being called via an observable.
Selectors
By default, all of the top-level properties from the initial state will be exposed as selectors which are computed
signals on the state object.
It is also possible to create more selectors simply using computed
and the values of the signal created by signalSlice
, however, it is awkward to have some selectors available directly on the state object (our default selectors) and others defined outside of the state object.
It is therefore recommended to define all of your selectors using the selectors
config of signalSlice
:
This will also make these additional computed values available on the state object:
Effects
To create side effects for state changes, you can use the standard Angular effect
to react to the state signal or selectors from signalSlice
changing, e.g:
If you intend to trigger another actionSource
from within your effects, it will be necessary to enable allowSignalWrites
as triggering an actionSource
will cause a value to be written to the state signal, e.g: