RFC: Integration with Angular Signals and NgRx packages #3843
Replies: 6 comments 11 replies
-
This all sounds really nice and simple, nothing complicated, and would be easy to migrate I think. I would like to raise one point though: In the eslint-plugin, we consider as one of the rules using @Component({...})
export class MyComponent {
store = inject(Store);
list$ = this.store.select(data).pipe(
map(data => data.list)
);
} and should be moved to a selector, with which I 100% agree, but here are some questions:
Otherwise, this is great, I'm super excited |
Beta Was this translation helpful? Give feedback.
-
Perfect, great-looking API! |
Beta Was this translation helpful? Give feedback.
-
Is there going to be any guidance around if we should be using |
Beta Was this translation helpful? Give feedback.
-
How about |
Beta Was this translation helpful? Give feedback.
-
This looks great, although export class MoviesComponent {
store = inject(Store);
movies = this.store.signal(selectAllMovies);
ngOnInit() {
this.store.dispatch(MoviesActions.enter());
}
} |
Beta Was this translation helpful? Give feedback.
-
Great work so far on integrating signals into the existing NgRx store libraries. I'm finding it difficult to settle on whether to access state (or slices of state) via signals or observables, within a ComponentStore. And when to convert from one to the other, especially when combining with derived state. I suppose this is the nature of the sync vs async complexities, and will be an ongoing discussion (with emerging patterns). A step that could help, for ComponentStore specifically, is to allow reuse of observable-based "selectors" with signal-based "selectors". I.e. allow the To give a concrete example, what I'd like to achieve is something like: // Note: in the "real thing" I model the state using TS discriminated unions, but the type below is enough for this demonstration.
type AuthState = {
status: 'disconnected' | 'connecting' | 'connected' | 'error',
user: User | null,
error: null,
};
type AuthViewModel = AuthState & { isAuthenticated: boolean };
const initialState: AuthState = {
status: 'disconnected',
user: null,
error: null,
};
@Injectable()
export class AuthStore extends ComponentStore<AuthState> implements OnStateInit {
readonly #authService = inject(AuthService);
// I _could_ do this, but I'd prefer to have an observable (which I can then use in the `waitUntilAuthConnected` helper, below).
// isAuthenticated = computed(() => {
// const state = this.state();
// return Boolean(state.user);
// });
readonly #isAuthenticated$ = this.select((state) => Boolean(state.user));
readonly isAuthenticated = toSignal(this.#isAuthenticated$, { initialValue: false });
// --> This is not possible right now as you can't combine signals and observables in `selectSignal`
readonly vm: Signal<AuthViewModel> = this.selectSignal(
this.state,
this.#isAuthenticated$,
(state, isAuthenticated) => ({
...state,
isAuthenticated,
}),
{ debounce: true },
);
// Helper function for use in guards, and anywhere else where it's essential for auth to be connected
waitUntilAuthConnected = () =>
this.state$.pipe(
filter(({ status }) => status === 'connected'),
switchMap(() => this.#isAuthenticated$),
take(1),
);
constructor() {
super(initialState);
console.log('AuthStore loaded');
}
ngrxOnStateInit(): void {
[…]
}
} Is this a reasonable ask? Or perhaps there's a better of achieving the same thing (i.e. a consolidated view model) whilst still having the flexibility of moving between signals and observables at will? |
Beta Was this translation helpful? Give feedback.
-
Purpose
This document serves to lay out the potential options for using Signals in Angular with NgRx libraries.
The RFCs for Angular Signals can be found here
Opportunities
Signals in Angular introduce a primitive for reactivity that's not built on top of RxJS. There is interoperability with RxJS observables and Signals through bridge functions such as
toSignal
andtoObservable
.Discussions
If you have feedback on the RFC, feel free to post a comment here, or join our Discord and join the #rfcs channel for semi-realtime longer discussions.
NgRx Store
The Redux pattern with NgRx Store serves as a type of reactive primitive in Angular. With NgRx libraries being built on top of observables, we can still 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 and associated libraries as Signals.
We improve this experience by adding a
selectSignal
method to the Store that takes a selector and returns a Signal of its value.NgRx ComponentStore
NgRx ComponentStore provides reactivity for local component state and side effects using observables. Integrating with Signals would provide interoperability 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 thecomputed
function.NOTE: Using lazy initialization of state would result in an error when reading a signal before the state is defined.
ComponentStore Effects are designed to extract any side-effects (such as Network calls) from components and handle potential race conditions.
In addition to static values, and observables, a ComponentStore effect would also support receiving a
Signal
as an input.As with observables, the effect function would execute every time the signal produces a value.
Impact on other NgRx packages
@ngrx/entity
@ngrx/store-devtools
@ngrx/router-store
@ngrx/component
Beta Was this translation helpful? Give feedback.
All reactions