All quizzesHard
Signal Store
Preview — 3 of 10 questions
What is the NgRx Signal Store and how does it differ from the global store?
javascript
import { signalStore, withState, withComputed, withMethods, withHooks } from '@ngrx/signals';
import { withEntities, setAll, removeEntity, updateEntity } from '@ngrx/signals/entities';
import { rxMethod } from '@ngrx/signals/rxjs-interop';
import { tapResponse } from '@ngrx/operators';
export const ProductStore = signalStore(
{ providedIn: 'root' }, // or scoped in component providers
// State slice
withState({
loading: false,
error: null as string | null,
filter: 'all' as 'all' | 'active' | 'inactive',
}),
// Entity state (normalized collection)
withEntities<Product>(),
// Computed signals (derived state)
withComputed(({ entities, filter }) => ({
filteredProducts: computed(() => {
const all = entities();
return filter() === 'all' ? all : all.filter(p => p.status === filter());
}),
activeCount: computed(() => entities().filter(p => p.active).length),
})),
// Methods (updaters + effects)
withMethods((store, productService = inject(ProductService)) => ({
// Synchronous updater
setFilter(filter: 'all' | 'active' | 'inactive') {
patchState(store, { filter });
},
// Async effect using rxMethod
loadProducts: rxMethod<void>(
pipe(
tap(() => patchState(store, { loading: true })),
exhaustMap(() =>
productService.getAll().pipe(
tapResponse({
next: products => patchState(store, setAll(products), { loading: false }),
error: (err: Error) => patchState(store, { error: err.message, loading: false }),
})
)
),
)
),
})),
// Lifecycle hooks
withHooks({
onInit(store) { store.loadProducts(); },
onDestroy(store) { console.log('Store destroyed'); },
}),
);
// Component usage:
@Component({ standalone: true })
export class ProductListComponent {
store = inject(ProductStore);
// All state is signals — no async pipe needed:
// store.filteredProducts() — computed signal
// store.loading() — state signal
// store.entities() — entity signal
}AIt is a wrapper around the global store that exposes state as signals
BA replacement for @ngrx/effects using signals instead of Observables
CA standalone, signal-first state management solution without actions/reducers boilerplate, using signalStore() factory with feature composition
DA global store that automatically converts all state values to signals
How do you create a reusable custom feature for NgRx Signal Store?
javascript
import { signalStoreFeature, withState, withMethods } from '@ngrx/signals';
import { patchState } from '@ngrx/signals';
// Reusable loading feature
interface LoadingState {
loading: boolean;
error: string | null;
}
export function withRequestStatus() {
return signalStoreFeature(
withState<LoadingState>({ loading: false, error: null }),
withComputed(({ loading, error }) => ({
isPending: loading,
hasError: computed(() => error() !== null),
})),
withMethods(store => ({
setLoading(): void {
patchState(store, { loading: true, error: null });
},
setSuccess(): void {
patchState(store, { loading: false, error: null });
},
setError(error: string): void {
patchState(store, { loading: false, error });
},
})),
);
}
// Reusable pagination feature
export function withPagination(initialPageSize = 20) {
return signalStoreFeature(
withState({ page: 1, pageSize: initialPageSize }),
withMethods(store => ({
nextPage(): void { patchState(store, { page: store.page() + 1 }); },
prevPage(): void { patchState(store, { page: Math.max(1, store.page() - 1) }); },
setPageSize(pageSize: number): void { patchState(store, { pageSize, page: 1 }); },
})),
);
}
// Using composed features:
export const ArticleStore = signalStore(
withEntities<Article>(),
withRequestStatus(), // adds loading/error state and methods
withPagination(10), // adds pagination state and methods
withMethods((store, articleService = inject(ArticleService)) => ({
loadArticles: rxMethod<void>(pipe(
tap(() => store.setLoading()),
switchMap(() => articleService.getPage(store.page(), store.pageSize()).pipe(
tapResponse({
next: result => { patchState(store, setAll(result.articles)); store.setSuccess(); },
error: (e: Error) => store.setError(e.message),
}),
)),
)),
})),
);AWrite a function using signalStoreFeature() that returns withState, withComputed, and withMethods compositions
BCreate a class that extends SignalStoreFeature<T>
CExport a @NgModule with store features configured
DCustom features are not supported — compose with existing with* functions only
How would you implement a meta-reducer that sends analytics events for specific actions?
javascript
import { MetaReducer, ActionReducer, INIT } from '@ngrx/store';
import { AnalyticsService } from './analytics.service';
// Factory function — allows service injection
export function analyticsMetaReducerFactory(
analyticsService: AnalyticsService
): MetaReducer<AppState> {
return (reducer: ActionReducer<AppState>): ActionReducer<AppState> => {
return (state, action) => {
// Track specific actions:
const trackedActions: string[] = [
'[Cart] Item Added',
'[Checkout] Purchase Completed',
'[Auth] User Logged In',
];
if (trackedActions.includes(action.type)) {
// Synchronous analytics (e.g., Google Tag Manager dataLayer push)
analyticsService.track(action.type, {
timestamp: Date.now(),
...('payload' in action ? action : {}),
});
}
return reducer(state, action);
};
};
}
// Registration:
export function provideAnalyticsMetaReducer(): EnvironmentProviders {
return makeEnvironmentProviders([
{
provide: META_REDUCERS,
useFactory: analyticsMetaReducerFactory,
deps: [AnalyticsService],
multi: true,
},
]);
}
// In app.config.ts:
provideStore(reducers),
provideAnalyticsMetaReducer(),AUse NgRx Effects with ofType() for all analytics actions
BWrap the reducer with a meta-reducer that intercepts specific actions and sends analytics synchronously before passing to the next reducer
CUse @ngrx/data to automatically track CRUD operations
DMeta-reducers cannot access services — use Effects for analytics
Sign up free to play
Answer all 10 questions (7 more), see explanations for every answer, and track your score.