All quizzesMedium
Effects & Selectors
Preview — 3 of 10 questions
What is an NgRx Effect and what problem does it solve?
javascript
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { inject } from '@angular/core';
import { switchMap, map, catchError } from 'rxjs/operators';
@Injectable()
export class UsersEffects {
private actions$ = inject(Actions);
private userService = inject(UserService);
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(loadUsers), // filter for this action type
switchMap(() => // cancel previous, subscribe to new
this.userService.getUsers().pipe(
map(users => loadUsersSuccess({ users })),
catchError(error => of(loadUsersFailure({ error: error.message }))),
)
),
)
);
// Effect that doesn't dispatch (side effect only)
logUserLoad$ = createEffect(() =>
this.actions$.pipe(
ofType(loadUsersSuccess),
tap(({ users }) => this.analytics.track('users_loaded', { count: users.length })),
),
{ dispatch: false } // ← required when not dispatching
);
}AAn Effect is a selector that computes derived state
BAn Effect is a lifecycle hook for the NgRx store
CAn Effect is a directive that renders store state in the template
DAn Effect is a class that handles side effects (HTTP calls, localStorage, analytics) triggered by actions, keeping reducers pure
What does @ngrx/entity provide?
javascript
import { createEntityAdapter, EntityState } from '@ngrx/entity';
// Entity adapter for User type
export const userAdapter = createEntityAdapter<User>({
selectId: user => user.id,
sortComparer: (a, b) => a.name.localeCompare(b.name),
});
// State with EntityState (ids + entities map)
export interface UsersState extends EntityState<User> {
loading: boolean;
error: string | null;
selectedUserId: string | null;
}
export const initialState: UsersState = userAdapter.getInitialState({
loading: false,
error: null,
selectedUserId: null,
});
// Reducer using adapter methods:
export const usersReducer = createReducer(
initialState,
on(loadUsersSuccess, (state, { users }) =>
userAdapter.setAll(users, { ...state, loading: false })
),
on(addUserSuccess, (state, { user }) =>
userAdapter.addOne(user, state)
),
on(updateUserSuccess, (state, { user }) =>
userAdapter.upsertOne(user, state)
),
on(deleteUserSuccess, (state, { id }) =>
userAdapter.removeOne(id, state)
),
);
// Built-in selectors from adapter:
export const {
selectAll: selectAllUsers,
selectEntities: selectUserEntities,
selectIds: selectUserIds,
selectTotal: selectUserCount,
} = userAdapter.getSelectors(selectUsersState);AA database ORM for Angular services
BA validation library for entity schemas
CA collection of pre-built reducer functions and selectors for managing normalized collections of entities (by ID) efficiently
DA factory for creating typed NgRx actions for CRUD operations
What is the correct way to create an action with multiple typed properties?
javascript
import { createAction, props } from '@ngrx/store';
// Action with typed payload
export const updateUserProfile = createAction(
'[User Profile] Update Profile',
props<{
userId: string;
changes: Partial<UserProfile>;
source: 'form' | 'api';
}>()
);
// Type-safe dispatch:
this.store.dispatch(updateUserProfile({
userId: 'user_123',
changes: { name: 'John', email: 'john@example.com' },
source: 'form',
}));
// TypeScript error if props don't match the type
// Action without payload:
export const clearCart = createAction('[Cart] Clear Cart');
this.store.dispatch(clearCart()); // no argument needed
// Accessing payload in reducer:
on(updateUserProfile, (state, { userId, changes, source }) => ({
...state,
users: state.users.map(u => u.id === userId ? { ...u, ...changes } : u),
})),AcreateAction('[Feature] Action', { prop1: string, prop2: number })
BcreateAction('[Feature] Action', props<{ prop1: string; prop2: number }>())
Cnew ActionCreator('[Feature] Action', ['prop1', 'prop2'])
DcreateAction('[Feature] Action').withProps<{ prop1: string; prop2: number }>()
Sign up free to play
Answer all 10 questions (7 more), see explanations for every answer, and track your score.