Creating Selectors
Selectors are pure functions that derive and memoize state slices. They only recompute when their inputs change.
Basic Selector
import { createFeatureSelector, createSelector } from '@ngrx/store';
// Feature selector
export const selectUserState = createFeatureSelector<UserState>('users');
// Simple selectors
export const selectUsers = createSelector(
selectUserState,
state => state.users
);
export const selectLoading = createSelector(
selectUserState,
state => state.loading
);
export const selectError = createSelector(
selectUserState,
state => state.error
);
Composing Selectors
export const selectActiveUsers = createSelector(
selectUsers,
users => users.filter(u => u.isActive)
);
export const selectUserCount = createSelector(
selectUsers,
users => users.length
);
export const selectUsersWithLoading = createSelector(
selectUsers,
selectLoading,
(users, loading) => ({ users, loading })
);
Using Selectors in Components
@Component({
selector: 'app-user-list',
template: `
<div *ngIf="loading$ | async">Loading...</div>
<p *ngFor="let user of users$ | async">{{ user.name }}</p>
`,
})
export class UserListComponent {
private store = inject(Store);
users$ = this.store.select(selectUsers);
loading$ = this.store.select(selectLoading);
}
Memoization and Performance
Selectors memoize results — they only recompute when their input selectors produce new values.
How Memoization Works
export const selectActiveUsers = createSelector(
selectUsers, // Input selector
users => users.filter(u => u.isActive) // Projector function
);
selectUsersruns and returnsusers- If
usersreference is the same as last time,selectActiveUsersreturns the cached result - If
usersreference changed, the projector function runs again
Selector with Parameters
export const selectUserById = (id: number) => createSelector(
selectUsers,
users => users.find(u => u.id === id)
);
// Usage
this.user$ = this.store.select(selectUserById(42));
Converting Selectors to Signals
import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-user-list',
template: `<p *ngFor="let user of users()">{{ user.name }}</p>`,
})
export class UserListComponent {
private store = inject(Store);
users = toSignal(this.store.select(selectUsers), { initialValue: [] });
}
Selector Best Practices
- Keep selectors small — Compose from smaller selectors
- Memoize expensive computations — createSelector caches results
- Use feature selectors — createFeatureSelector for state slices
- Avoid logic in components — Move derivation to selectors
Quiz
1. What does createSelector do?
2. How do you convert a selector to a signal?
3. What is a feature selector?
Flashcards
Question
What does createSelector do?
Click to reveal answer
Answer
Creates a memoized selector. Only recomputes when inputs change.
Question
How do you convert a selector to a signal?
Click to reveal answer
Answer
toSignal(store.select(selector), { initialValue: [] })
Question
What is a feature selector?
Click to reveal answer
Answer
createFeatureSelector<State>('name') — selects a top-level state slice.
Revision Notes
Key Takeaways
- 1. createSelector memoizes derived state
- 2. Compose selectors for complex derivations
- 3. toSignal converts selectors to signals
- 4. Feature selectors select top-level state slices
Interview Tips
- • Explain selector memoization
- • Be ready to describe selector composition
- • Mention toSignal for signal-based reads
Cheat Sheet
Selectors Cheat Sheet
Feature selector:
export const selectUserState = createFeatureSelector<UserState>('users');
Simple selector:
export const selectUsers = createSelector(
selectUserState,
state => state.users
);
Composed selector:
export const selectActiveUsers = createSelector(
selectUsers,
users => users.filter(u => u.isActive)
);
To signal:
users = toSignal(this.store.select(selectUsers), { initialValue: [] });