You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I attended the SignalStore workshop at ng-conf 2024, and I found it to be one of the more useful bits of my conference experience. It's an exciting technology emerging at an exciting time for Angular. Contrats!
Problem Statement
Our small team has been experimenting with using the SignalStore in one of our applications. In some of the earlier experiments, we were working with a collection of entities. In that case, we were able to use the withEntities feature of the store, and found an intuitive, maintainable, and scalable API was exposed for working with our state.
Building on that success, we're now experimenting with using @ngrx/signals in some other portions of our application. The page we're experimenting with now is a large object for a "detail" page, not a collection of entities. The data for the page is not fetched until the user has landed on this detail page. Users can be linked to this page from many different sections of the larger application.
Given the asynchronous nature of the data, it feels like it would be beneficial to represent the absence of data in our application's types. When using an rxjsBehaviorSubject, we've often defined our type as BehaviorSubject<Entity | null>(null). The intent is to communicate via the type system that the data is not always present, so guard your views appropriately. IMO, this is one of the benefits of strong typing.
When trying to carry that concept over to our signal store usage, we're finding things don't necessarily meet our expectations. Many of the examples I have encountered that are similar to our situation tend to use initial state to satisfy strong typing. For example, you may have a type and initial state like this example:
On the surface, this seems to provide the expected developer experience with the signal store. However, I feel like the empty strings and meaningful ID of 0 are footguns. These properties will present as type string in TypeScript, when arguably string | null | undefined is a better representation. This could lead to an undocumented need for "truthy" checks on our properties. As another solution, we have experimented with setting up nullable types in our State, like so:
While I feel this better models our data flow in the type system, it has the effect of eliminating the DeepSignal-ness of our object. For example:
this.nullableUserStore.user()?.address.place;// ^? string | undefined// we've lost our `DeepSignal<string>` that I would hope to exist on `address.place`
Example
Here's an example repository I've put together. Hopefully it helps to communicate what we're trying to accomplish with the SignalStore, and some of the pain points we've encountered. While the example focuses on a User object, we are looking at the SignalStore for use with domain-specific entity management: https://github.com/vitale232/signal-store-play/
Discussion
I have a few questions for discussion:
Is the SignalStore an appropriate technology choice for a use case like this?
What are the emerging practices to represent nullability in the SignalStore?
Is it best to provide an initial state like our NonNullableUserStore in the example, and try to use other features like withRequestStatus to guard views and account for missing data?
Are my concerns about the misleading types misguided in practice?
I'm curious what others are doing to address these concerns, or if they are even valid!
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
I attended the SignalStore workshop at ng-conf 2024, and I found it to be one of the more useful bits of my conference experience. It's an exciting technology emerging at an exciting time for Angular. Contrats!
Problem Statement
Our small team has been experimenting with using the SignalStore in one of our applications. In some of the earlier experiments, we were working with a collection of entities. In that case, we were able to use the
withEntities
feature of the store, and found an intuitive, maintainable, and scalable API was exposed for working with our state.Building on that success, we're now experimenting with using
@ngrx/signals
in some other portions of our application. The page we're experimenting with now is a large object for a "detail" page, not a collection of entities. The data for the page is not fetched until the user has landed on this detail page. Users can be linked to this page from many different sections of the larger application.Given the asynchronous nature of the data, it feels like it would be beneficial to represent the absence of data in our application's types. When using an
rxjs
BehaviorSubject
, we've often defined our type asBehaviorSubject<Entity | null>(null)
. The intent is to communicate via the type system that the data is not always present, so guard your views appropriately. IMO, this is one of the benefits of strong typing.When trying to carry that concept over to our signal store usage, we're finding things don't necessarily meet our expectations. Many of the examples I have encountered that are similar to our situation tend to use initial state to satisfy strong typing. For example, you may have a type and initial state like this example:
On the surface, this seems to provide the expected developer experience with the signal store. However, I feel like the empty strings and meaningful ID of 0 are footguns. These properties will present as type
string
in TypeScript, when arguablystring | null | undefined
is a better representation. This could lead to an undocumented need for "truthy" checks on our properties. As another solution, we have experimented with setting up nullable types in ourState
, like so:While I feel this better models our data flow in the type system, it has the effect of eliminating the
DeepSignal
-ness of our object. For example:Example
Here's an example repository I've put together. Hopefully it helps to communicate what we're trying to accomplish with the SignalStore, and some of the pain points we've encountered. While the example focuses on a User object, we are looking at the SignalStore for use with domain-specific entity management:
https://github.com/vitale232/signal-store-play/
Discussion
I have a few questions for discussion:
NonNullableUserStore
in the example, and try to use other features likewithRequestStatus
to guard views and account for missing data?I'm curious what others are doing to address these concerns, or if they are even valid!
Beta Was this translation helpful? Give feedback.
All reactions